xref: /freebsd/sys/net/if_infiniband.c (revision 1d3a22e765c0d4a594adda5a0763524efde140bf)
19d40cf60SHans Petter Selasky /*-
29d40cf60SHans Petter Selasky  * Copyright (c) 2020 Mellanox Technologies. All rights reserved.
39d40cf60SHans Petter Selasky  *
49d40cf60SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
59d40cf60SHans Petter Selasky  * modification, are permitted provided that the following conditions
69d40cf60SHans Petter Selasky  * are met:
79d40cf60SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
89d40cf60SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer.
99d40cf60SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
109d40cf60SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
119d40cf60SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
129d40cf60SHans Petter Selasky  *
139d40cf60SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
149d40cf60SHans Petter Selasky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
159d40cf60SHans Petter Selasky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
169d40cf60SHans Petter Selasky  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
179d40cf60SHans Petter Selasky  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
189d40cf60SHans Petter Selasky  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
199d40cf60SHans Petter Selasky  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
209d40cf60SHans Petter Selasky  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
219d40cf60SHans Petter Selasky  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
229d40cf60SHans Petter Selasky  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
239d40cf60SHans Petter Selasky  * SUCH DAMAGE.
249d40cf60SHans Petter Selasky  */
259d40cf60SHans Petter Selasky 
269d40cf60SHans Petter Selasky #include "opt_inet.h"
279d40cf60SHans Petter Selasky #include "opt_inet6.h"
289d40cf60SHans Petter Selasky 
299d40cf60SHans Petter Selasky #include <sys/cdefs.h>
309d40cf60SHans Petter Selasky __FBSDID("$FreeBSD$");
319d40cf60SHans Petter Selasky 
32ce329aa2SHans Petter Selasky #include <sys/param.h>
33*1d3a22e7SHans Petter Selasky #include <sys/systm.h>
3401630a49SHans Petter Selasky #include <sys/devctl.h>
359d40cf60SHans Petter Selasky #include <sys/eventhandler.h>
3601630a49SHans Petter Selasky #include <sys/kernel.h>
3701630a49SHans Petter Selasky #include <sys/mbuf.h>
3801630a49SHans Petter Selasky #include <sys/module.h>
399d40cf60SHans Petter Selasky #include <sys/socket.h>
409d40cf60SHans Petter Selasky #include <sys/sysctl.h>
419d40cf60SHans Petter Selasky 
4201630a49SHans Petter Selasky #include <net/bpf.h>
439d40cf60SHans Petter Selasky #include <net/ethernet.h>
449d40cf60SHans Petter Selasky #include <net/infiniband.h>
4501630a49SHans Petter Selasky #include <net/if.h>
4601630a49SHans Petter Selasky #include <net/if_var.h>
479d40cf60SHans Petter Selasky #include <net/if_dl.h>
489d40cf60SHans Petter Selasky #include <net/if_media.h>
49a92c4bb6SHans Petter Selasky #include <net/if_lagg.h>
5001630a49SHans Petter Selasky #include <net/if_llatbl.h>
5101630a49SHans Petter Selasky #include <net/if_types.h>
5201630a49SHans Petter Selasky #include <net/netisr.h>
5301630a49SHans Petter Selasky #include <net/route.h>
549d40cf60SHans Petter Selasky #include <netinet/if_ether.h>
5501630a49SHans Petter Selasky #include <netinet/in.h>
569d40cf60SHans Petter Selasky #include <netinet/ip6.h>
579d40cf60SHans Petter Selasky #include <netinet6/in6_var.h>
589d40cf60SHans Petter Selasky #include <netinet6/nd6.h>
599d40cf60SHans Petter Selasky 
609d40cf60SHans Petter Selasky #include <security/mac/mac_framework.h>
619d40cf60SHans Petter Selasky 
62a92c4bb6SHans Petter Selasky /* if_lagg(4) support */
63a92c4bb6SHans Petter Selasky struct mbuf *(*lagg_input_infiniband_p)(struct ifnet *, struct mbuf *);
64a92c4bb6SHans Petter Selasky 
659d40cf60SHans Petter Selasky #ifdef INET
669d40cf60SHans Petter Selasky static inline void
6701630a49SHans Petter Selasky infiniband_ipv4_multicast_map(
6801630a49SHans Petter Selasky     uint32_t addr, const uint8_t *broadcast, uint8_t *buf)
699d40cf60SHans Petter Selasky {
709d40cf60SHans Petter Selasky 	uint8_t scope;
719d40cf60SHans Petter Selasky 
729d40cf60SHans Petter Selasky 	addr = ntohl(addr);
739d40cf60SHans Petter Selasky 	scope = broadcast[5] & 0xF;
749d40cf60SHans Petter Selasky 
759d40cf60SHans Petter Selasky 	buf[0] = 0;
769d40cf60SHans Petter Selasky 	buf[1] = 0xff;
779d40cf60SHans Petter Selasky 	buf[2] = 0xff;
789d40cf60SHans Petter Selasky 	buf[3] = 0xff;
799d40cf60SHans Petter Selasky 	buf[4] = 0xff;
809d40cf60SHans Petter Selasky 	buf[5] = 0x10 | scope;
819d40cf60SHans Petter Selasky 	buf[6] = 0x40;
829d40cf60SHans Petter Selasky 	buf[7] = 0x1b;
839d40cf60SHans Petter Selasky 	buf[8] = broadcast[8];
849d40cf60SHans Petter Selasky 	buf[9] = broadcast[9];
859d40cf60SHans Petter Selasky 	buf[10] = 0;
869d40cf60SHans Petter Selasky 	buf[11] = 0;
879d40cf60SHans Petter Selasky 	buf[12] = 0;
889d40cf60SHans Petter Selasky 	buf[13] = 0;
899d40cf60SHans Petter Selasky 	buf[14] = 0;
909d40cf60SHans Petter Selasky 	buf[15] = 0;
919d40cf60SHans Petter Selasky 	buf[16] = (addr >> 24) & 0xff;
929d40cf60SHans Petter Selasky 	buf[17] = (addr >> 16) & 0xff;
939d40cf60SHans Petter Selasky 	buf[18] = (addr >> 8) & 0xff;
949d40cf60SHans Petter Selasky 	buf[19] = addr & 0xff;
959d40cf60SHans Petter Selasky }
969d40cf60SHans Petter Selasky #endif
979d40cf60SHans Petter Selasky 
989d40cf60SHans Petter Selasky #ifdef INET6
999d40cf60SHans Petter Selasky static inline void
10001630a49SHans Petter Selasky infiniband_ipv6_multicast_map(
10101630a49SHans Petter Selasky     const struct in6_addr *addr, const uint8_t *broadcast, uint8_t *buf)
1029d40cf60SHans Petter Selasky {
1039d40cf60SHans Petter Selasky 	uint8_t scope;
1049d40cf60SHans Petter Selasky 
1059d40cf60SHans Petter Selasky 	scope = broadcast[5] & 0xF;
1069d40cf60SHans Petter Selasky 
1079d40cf60SHans Petter Selasky 	buf[0] = 0;
1089d40cf60SHans Petter Selasky 	buf[1] = 0xff;
1099d40cf60SHans Petter Selasky 	buf[2] = 0xff;
1109d40cf60SHans Petter Selasky 	buf[3] = 0xff;
1119d40cf60SHans Petter Selasky 	buf[4] = 0xff;
1129d40cf60SHans Petter Selasky 	buf[5] = 0x10 | scope;
1139d40cf60SHans Petter Selasky 	buf[6] = 0x60;
1149d40cf60SHans Petter Selasky 	buf[7] = 0x1b;
1159d40cf60SHans Petter Selasky 	buf[8] = broadcast[8];
1169d40cf60SHans Petter Selasky 	buf[9] = broadcast[9];
1179d40cf60SHans Petter Selasky 	memcpy(&buf[10], &addr->s6_addr[6], 10);
1189d40cf60SHans Petter Selasky }
1199d40cf60SHans Petter Selasky #endif
1209d40cf60SHans Petter Selasky 
1219d40cf60SHans Petter Selasky /*
1229d40cf60SHans Petter Selasky  * This is for clients that have an infiniband_header in the mbuf.
1239d40cf60SHans Petter Selasky  */
1249d40cf60SHans Petter Selasky void
1259d40cf60SHans Petter Selasky infiniband_bpf_mtap(struct ifnet *ifp, struct mbuf *mb)
1269d40cf60SHans Petter Selasky {
1279d40cf60SHans Petter Selasky 	struct infiniband_header *ibh;
1289d40cf60SHans Petter Selasky 	struct ether_header eh;
1299d40cf60SHans Petter Selasky 
1309d40cf60SHans Petter Selasky 	if (mb->m_len < sizeof(*ibh))
1319d40cf60SHans Petter Selasky 		return;
1329d40cf60SHans Petter Selasky 
1339d40cf60SHans Petter Selasky 	ibh = mtod(mb, struct infiniband_header *);
1349d40cf60SHans Petter Selasky 	eh.ether_type = ibh->ib_protocol;
1359d40cf60SHans Petter Selasky 	memset(eh.ether_shost, 0, ETHER_ADDR_LEN);
1369d40cf60SHans Petter Selasky 	memcpy(eh.ether_dhost, ibh->ib_hwaddr + 4, ETHER_ADDR_LEN);
1379d40cf60SHans Petter Selasky 	mb->m_data += sizeof(*ibh);
1389d40cf60SHans Petter Selasky 	mb->m_len -= sizeof(*ibh);
1399d40cf60SHans Petter Selasky 	mb->m_pkthdr.len -= sizeof(*ibh);
1409d40cf60SHans Petter Selasky 	bpf_mtap2(ifp->if_bpf, &eh, sizeof(eh), mb);
1419d40cf60SHans Petter Selasky 	mb->m_data -= sizeof(*ibh);
1429d40cf60SHans Petter Selasky 	mb->m_len += sizeof(*ibh);
1439d40cf60SHans Petter Selasky 	mb->m_pkthdr.len += sizeof(*ibh);
1449d40cf60SHans Petter Selasky }
1459d40cf60SHans Petter Selasky 
1469d40cf60SHans Petter Selasky /*
1479d40cf60SHans Petter Selasky  * Infiniband output routine.
1489d40cf60SHans Petter Selasky  */
1499d40cf60SHans Petter Selasky static int
1509d40cf60SHans Petter Selasky infiniband_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
1519d40cf60SHans Petter Selasky     struct route *ro)
1529d40cf60SHans Petter Selasky {
1539d40cf60SHans Petter Selasky 	uint8_t edst[INFINIBAND_ADDR_LEN];
1549d40cf60SHans Petter Selasky #if defined(INET) || defined(INET6)
1559d40cf60SHans Petter Selasky 	struct llentry *lle = NULL;
1569d40cf60SHans Petter Selasky #endif
1579d40cf60SHans Petter Selasky 	struct infiniband_header *ibh;
1589d40cf60SHans Petter Selasky 	int error = 0;
1599d40cf60SHans Petter Selasky 	uint16_t type;
1609d40cf60SHans Petter Selasky 	bool is_gw;
1619d40cf60SHans Petter Selasky 
1629d40cf60SHans Petter Selasky 	NET_EPOCH_ASSERT();
1639d40cf60SHans Petter Selasky 
1649d40cf60SHans Petter Selasky 	is_gw = ((ro != NULL) && (ro->ro_flags & RT_HAS_GW) != 0);
1659d40cf60SHans Petter Selasky 
1669d40cf60SHans Petter Selasky #ifdef MAC
1679d40cf60SHans Petter Selasky 	error = mac_ifnet_check_transmit(ifp, m);
1689d40cf60SHans Petter Selasky 	if (error)
1699d40cf60SHans Petter Selasky 		goto bad;
1709d40cf60SHans Petter Selasky #endif
1719d40cf60SHans Petter Selasky 
1729d40cf60SHans Petter Selasky 	M_PROFILE(m);
1739d40cf60SHans Petter Selasky 	if (ifp->if_flags & IFF_MONITOR) {
1749d40cf60SHans Petter Selasky 		error = ENETDOWN;
1759d40cf60SHans Petter Selasky 		goto bad;
1769d40cf60SHans Petter Selasky 	}
1779d40cf60SHans Petter Selasky 	if (!((ifp->if_flags & IFF_UP) &&
1789d40cf60SHans Petter Selasky 	    (ifp->if_drv_flags & IFF_DRV_RUNNING))) {
1799d40cf60SHans Petter Selasky 		error = ENETDOWN;
1809d40cf60SHans Petter Selasky 		goto bad;
1819d40cf60SHans Petter Selasky 	}
1829d40cf60SHans Petter Selasky 
1839d40cf60SHans Petter Selasky 	switch (dst->sa_family) {
1849d40cf60SHans Petter Selasky 	case AF_LINK:
1859d40cf60SHans Petter Selasky 		goto output;
1869d40cf60SHans Petter Selasky #ifdef INET
1879d40cf60SHans Petter Selasky 	case AF_INET:
1889d40cf60SHans Petter Selasky 		if (lle != NULL && (lle->la_flags & LLE_VALID)) {
1899d40cf60SHans Petter Selasky 			memcpy(edst, lle->ll_addr, sizeof(edst));
1909d40cf60SHans Petter Selasky 		} else if (m->m_flags & M_MCAST) {
1919d40cf60SHans Petter Selasky 			infiniband_ipv4_multicast_map(
1929d40cf60SHans Petter Selasky 			    ((const struct sockaddr_in *)dst)->sin_addr.s_addr,
1939d40cf60SHans Petter Selasky 			    ifp->if_broadcastaddr, edst);
1949d40cf60SHans Petter Selasky 		} else {
1959d40cf60SHans Petter Selasky 			error = arpresolve(ifp, is_gw, m, dst, edst, NULL, NULL);
1969d40cf60SHans Petter Selasky 			if (error) {
1979d40cf60SHans Petter Selasky 				if (error == EWOULDBLOCK)
1989d40cf60SHans Petter Selasky 					error = 0;
1999d40cf60SHans Petter Selasky 				m = NULL; /* mbuf is consumed by resolver */
2009d40cf60SHans Petter Selasky 				goto bad;
2019d40cf60SHans Petter Selasky 			}
2029d40cf60SHans Petter Selasky 		}
2039d40cf60SHans Petter Selasky 		type = htons(ETHERTYPE_IP);
2049d40cf60SHans Petter Selasky 		break;
2059d40cf60SHans Petter Selasky 	case AF_ARP: {
2069d40cf60SHans Petter Selasky 		struct arphdr *ah;
2079d40cf60SHans Petter Selasky 
2089d40cf60SHans Petter Selasky 		if (m->m_len < sizeof(*ah)) {
2099d40cf60SHans Petter Selasky 			error = EINVAL;
2109d40cf60SHans Petter Selasky 			goto bad;
2119d40cf60SHans Petter Selasky 		}
2129d40cf60SHans Petter Selasky 
2139d40cf60SHans Petter Selasky 		ah = mtod(m, struct arphdr *);
2149d40cf60SHans Petter Selasky 
2159d40cf60SHans Petter Selasky 		if (m->m_len < arphdr_len(ah)) {
2169d40cf60SHans Petter Selasky 			error = EINVAL;
2179d40cf60SHans Petter Selasky 			goto bad;
2189d40cf60SHans Petter Selasky 		}
2199d40cf60SHans Petter Selasky 		ah->ar_hrd = htons(ARPHRD_INFINIBAND);
2209d40cf60SHans Petter Selasky 
2219d40cf60SHans Petter Selasky 		switch (ntohs(ah->ar_op)) {
2229d40cf60SHans Petter Selasky 		case ARPOP_REVREQUEST:
2239d40cf60SHans Petter Selasky 		case ARPOP_REVREPLY:
2249d40cf60SHans Petter Selasky 			type = htons(ETHERTYPE_REVARP);
2259d40cf60SHans Petter Selasky 			break;
2269d40cf60SHans Petter Selasky 		case ARPOP_REQUEST:
2279d40cf60SHans Petter Selasky 		case ARPOP_REPLY:
2289d40cf60SHans Petter Selasky 		default:
2299d40cf60SHans Petter Selasky 			type = htons(ETHERTYPE_ARP);
2309d40cf60SHans Petter Selasky 			break;
2319d40cf60SHans Petter Selasky 		}
2329d40cf60SHans Petter Selasky 
2339d40cf60SHans Petter Selasky 		if (m->m_flags & M_BCAST) {
2349d40cf60SHans Petter Selasky 			memcpy(edst, ifp->if_broadcastaddr, INFINIBAND_ADDR_LEN);
2359d40cf60SHans Petter Selasky 		} else {
2369d40cf60SHans Petter Selasky 			if (ah->ar_hln != INFINIBAND_ADDR_LEN) {
2379d40cf60SHans Petter Selasky 				error = EINVAL;
2389d40cf60SHans Petter Selasky 				goto bad;
2399d40cf60SHans Petter Selasky 			}
2409d40cf60SHans Petter Selasky 			memcpy(edst, ar_tha(ah), INFINIBAND_ADDR_LEN);
2419d40cf60SHans Petter Selasky 		}
2429d40cf60SHans Petter Selasky 		break;
2439d40cf60SHans Petter Selasky 	}
2449d40cf60SHans Petter Selasky #endif
2459d40cf60SHans Petter Selasky #ifdef INET6
2469d40cf60SHans Petter Selasky 	case AF_INET6: {
2479d40cf60SHans Petter Selasky 		const struct ip6_hdr *ip6;
2489d40cf60SHans Petter Selasky 
2499d40cf60SHans Petter Selasky 		ip6 = mtod(m, const struct ip6_hdr *);
2509d40cf60SHans Petter Selasky 		if (m->m_len < sizeof(*ip6)) {
2519d40cf60SHans Petter Selasky 			error = EINVAL;
2529d40cf60SHans Petter Selasky 			goto bad;
2539d40cf60SHans Petter Selasky 		} else if (lle != NULL && (lle->la_flags & LLE_VALID)) {
2549d40cf60SHans Petter Selasky 			memcpy(edst, lle->ll_addr, sizeof(edst));
2559d40cf60SHans Petter Selasky 		} else if (m->m_flags & M_MCAST) {
2569d40cf60SHans Petter Selasky 			infiniband_ipv6_multicast_map(
2579d40cf60SHans Petter Selasky 			    &((const struct sockaddr_in6 *)dst)->sin6_addr,
2589d40cf60SHans Petter Selasky 			    ifp->if_broadcastaddr, edst);
2599d40cf60SHans Petter Selasky 		} else if (ip6->ip6_nxt == IPPROTO_ICMPV6) {
2609d40cf60SHans Petter Selasky 			memcpy(edst, ifp->if_broadcastaddr, INFINIBAND_ADDR_LEN);
2619d40cf60SHans Petter Selasky 		} else {
2629d40cf60SHans Petter Selasky 			error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL, NULL);
2639d40cf60SHans Petter Selasky 			if (error) {
2649d40cf60SHans Petter Selasky 				if (error == EWOULDBLOCK)
2659d40cf60SHans Petter Selasky 					error = 0;
2669d40cf60SHans Petter Selasky 				m = NULL; /* mbuf is consumed by resolver */
2679d40cf60SHans Petter Selasky 				goto bad;
2689d40cf60SHans Petter Selasky 			}
2699d40cf60SHans Petter Selasky 		}
2709d40cf60SHans Petter Selasky 		type = htons(ETHERTYPE_IPV6);
2719d40cf60SHans Petter Selasky 		break;
2729d40cf60SHans Petter Selasky 	}
2739d40cf60SHans Petter Selasky #endif
2749d40cf60SHans Petter Selasky 	default:
2759d40cf60SHans Petter Selasky 		error = EAFNOSUPPORT;
2769d40cf60SHans Petter Selasky 		goto bad;
2779d40cf60SHans Petter Selasky 	}
2789d40cf60SHans Petter Selasky 
2799d40cf60SHans Petter Selasky 	/*
2809d40cf60SHans Petter Selasky 	 * Add local net header.  If no space in first mbuf,
2819d40cf60SHans Petter Selasky 	 * allocate another.
2829d40cf60SHans Petter Selasky 	 */
2839d40cf60SHans Petter Selasky 	M_PREPEND(m, INFINIBAND_HDR_LEN, M_NOWAIT);
2849d40cf60SHans Petter Selasky 	if (m == NULL) {
2859d40cf60SHans Petter Selasky 		error = ENOBUFS;
2869d40cf60SHans Petter Selasky 		goto bad;
2879d40cf60SHans Petter Selasky 	}
2889d40cf60SHans Petter Selasky 	ibh = mtod(m, struct infiniband_header *);
2899d40cf60SHans Petter Selasky 
2909d40cf60SHans Petter Selasky 	ibh->ib_protocol = type;
2919d40cf60SHans Petter Selasky 	memcpy(ibh->ib_hwaddr, edst, sizeof(edst));
2929d40cf60SHans Petter Selasky 
2939d40cf60SHans Petter Selasky 	/*
2949d40cf60SHans Petter Selasky 	 * Queue message on interface, update output statistics if
2959d40cf60SHans Petter Selasky 	 * successful, and start output if interface not yet active.
2969d40cf60SHans Petter Selasky 	 */
2979d40cf60SHans Petter Selasky output:
2989d40cf60SHans Petter Selasky 	return (ifp->if_transmit(ifp, m));
2999d40cf60SHans Petter Selasky bad:
3009d40cf60SHans Petter Selasky 	if (m != NULL)
3019d40cf60SHans Petter Selasky 		m_freem(m);
3029d40cf60SHans Petter Selasky 	return (error);
3039d40cf60SHans Petter Selasky }
3049d40cf60SHans Petter Selasky 
3059d40cf60SHans Petter Selasky /*
3069d40cf60SHans Petter Selasky  * Process a received Infiniband packet.
3079d40cf60SHans Petter Selasky  */
3089d40cf60SHans Petter Selasky static void
3099d40cf60SHans Petter Selasky infiniband_input(struct ifnet *ifp, struct mbuf *m)
3109d40cf60SHans Petter Selasky {
3119d40cf60SHans Petter Selasky 	struct infiniband_header *ibh;
3129d40cf60SHans Petter Selasky 	struct epoch_tracker et;
3139d40cf60SHans Petter Selasky 	int isr;
3149d40cf60SHans Petter Selasky 
3159d40cf60SHans Petter Selasky 	CURVNET_SET_QUIET(ifp->if_vnet);
3169d40cf60SHans Petter Selasky 
3179d40cf60SHans Petter Selasky 	if ((ifp->if_flags & IFF_UP) == 0) {
3189d40cf60SHans Petter Selasky 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
3199d40cf60SHans Petter Selasky 		m_freem(m);
3209d40cf60SHans Petter Selasky 		goto done;
3219d40cf60SHans Petter Selasky 	}
3229d40cf60SHans Petter Selasky 
3239d40cf60SHans Petter Selasky 	ibh = mtod(m, struct infiniband_header *);
3249d40cf60SHans Petter Selasky 
3259d40cf60SHans Petter Selasky 	/*
3269d40cf60SHans Petter Selasky 	 * Reset layer specific mbuf flags to avoid confusing upper
3279d40cf60SHans Petter Selasky 	 * layers:
3289d40cf60SHans Petter Selasky 	 */
3299d40cf60SHans Petter Selasky 	m->m_flags &= ~M_VLANTAG;
3309d40cf60SHans Petter Selasky 	m_clrprotoflags(m);
3319d40cf60SHans Petter Selasky 
3329d40cf60SHans Petter Selasky 	if (INFINIBAND_IS_MULTICAST(ibh->ib_hwaddr)) {
3339d40cf60SHans Petter Selasky 		if (memcmp(ibh->ib_hwaddr, ifp->if_broadcastaddr,
3349d40cf60SHans Petter Selasky 		    ifp->if_addrlen) == 0)
3359d40cf60SHans Petter Selasky 			m->m_flags |= M_BCAST;
3369d40cf60SHans Petter Selasky 		else
3379d40cf60SHans Petter Selasky 			m->m_flags |= M_MCAST;
3389d40cf60SHans Petter Selasky 		if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1);
3399d40cf60SHans Petter Selasky 	}
3409d40cf60SHans Petter Selasky 
3419d40cf60SHans Petter Selasky 	/* Let BPF have it before we strip the header. */
3429d40cf60SHans Petter Selasky 	INFINIBAND_BPF_MTAP(ifp, m);
3439d40cf60SHans Petter Selasky 
3449d40cf60SHans Petter Selasky 	/* Allow monitor mode to claim this frame, after stats are updated. */
3459d40cf60SHans Petter Selasky 	if (ifp->if_flags & IFF_MONITOR) {
3469d40cf60SHans Petter Selasky 		m_freem(m);
3479d40cf60SHans Petter Selasky 		goto done;
3489d40cf60SHans Petter Selasky 	}
3499d40cf60SHans Petter Selasky 
3509d40cf60SHans Petter Selasky 	/* Direct packet to correct FIB based on interface config. */
3519d40cf60SHans Petter Selasky 	M_SETFIB(m, ifp->if_fib);
3529d40cf60SHans Petter Selasky 
353a92c4bb6SHans Petter Selasky 	/* Handle input from a lagg<N> port */
354a92c4bb6SHans Petter Selasky 	if (ifp->if_type == IFT_INFINIBANDLAG) {
355a92c4bb6SHans Petter Selasky 		KASSERT(lagg_input_infiniband_p != NULL,
356a92c4bb6SHans Petter Selasky 		    ("%s: if_lagg not loaded!", __func__));
357a92c4bb6SHans Petter Selasky 		m = (*lagg_input_infiniband_p)(ifp, m);
358a92c4bb6SHans Petter Selasky 		if (__predict_false(m == NULL))
359a92c4bb6SHans Petter Selasky 			goto done;
360a92c4bb6SHans Petter Selasky 		ifp = m->m_pkthdr.rcvif;
361a92c4bb6SHans Petter Selasky 	}
362a92c4bb6SHans Petter Selasky 
3639d40cf60SHans Petter Selasky 	/*
3649d40cf60SHans Petter Selasky 	 * Dispatch frame to upper layer.
3659d40cf60SHans Petter Selasky 	 */
3669d40cf60SHans Petter Selasky 	switch (ibh->ib_protocol) {
3679d40cf60SHans Petter Selasky #ifdef INET
3689d40cf60SHans Petter Selasky 	case htons(ETHERTYPE_IP):
3699d40cf60SHans Petter Selasky 		isr = NETISR_IP;
3709d40cf60SHans Petter Selasky 		break;
3719d40cf60SHans Petter Selasky 
3729d40cf60SHans Petter Selasky 	case htons(ETHERTYPE_ARP):
3739d40cf60SHans Petter Selasky 		if (ifp->if_flags & IFF_NOARP) {
3749d40cf60SHans Petter Selasky 			/* Discard packet if ARP is disabled on interface */
3759d40cf60SHans Petter Selasky 			m_freem(m);
3769d40cf60SHans Petter Selasky 			goto done;
3779d40cf60SHans Petter Selasky 		}
3789d40cf60SHans Petter Selasky 		isr = NETISR_ARP;
3799d40cf60SHans Petter Selasky 		break;
3809d40cf60SHans Petter Selasky #endif
3819d40cf60SHans Petter Selasky #ifdef INET6
3829d40cf60SHans Petter Selasky 	case htons(ETHERTYPE_IPV6):
3839d40cf60SHans Petter Selasky 		isr = NETISR_IPV6;
3849d40cf60SHans Petter Selasky 		break;
3859d40cf60SHans Petter Selasky #endif
3869d40cf60SHans Petter Selasky 	default:
3879d40cf60SHans Petter Selasky 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
3889d40cf60SHans Petter Selasky 		m_freem(m);
3899d40cf60SHans Petter Selasky 		goto done;
3909d40cf60SHans Petter Selasky 	}
3919d40cf60SHans Petter Selasky 
3929d40cf60SHans Petter Selasky 	/* Strip off the Infiniband header. */
3939d40cf60SHans Petter Selasky 	m_adj(m, INFINIBAND_HDR_LEN);
3949d40cf60SHans Petter Selasky 
3959d40cf60SHans Petter Selasky #ifdef MAC
3969d40cf60SHans Petter Selasky 	/*
3979d40cf60SHans Petter Selasky 	 * Tag the mbuf with an appropriate MAC label before any other
3989d40cf60SHans Petter Selasky 	 * consumers can get to it.
3999d40cf60SHans Petter Selasky 	 */
4009d40cf60SHans Petter Selasky 	mac_ifnet_create_mbuf(ifp, m);
4019d40cf60SHans Petter Selasky #endif
4029d40cf60SHans Petter Selasky 	/* Allow monitor mode to claim this frame, after stats are updated. */
4039d40cf60SHans Petter Selasky 	NET_EPOCH_ENTER(et);
4049d40cf60SHans Petter Selasky 	netisr_dispatch(isr, m);
4059d40cf60SHans Petter Selasky 	NET_EPOCH_EXIT(et);
4069d40cf60SHans Petter Selasky done:
4079d40cf60SHans Petter Selasky 	CURVNET_RESTORE();
4089d40cf60SHans Petter Selasky }
4099d40cf60SHans Petter Selasky 
4109d40cf60SHans Petter Selasky static int
41101630a49SHans Petter Selasky infiniband_resolvemulti(
41201630a49SHans Petter Selasky     struct ifnet *ifp, struct sockaddr **llsa, struct sockaddr *sa)
4139d40cf60SHans Petter Selasky {
4149d40cf60SHans Petter Selasky 	struct sockaddr_dl *sdl;
4159d40cf60SHans Petter Selasky #ifdef INET
4169d40cf60SHans Petter Selasky 	struct sockaddr_in *sin;
4179d40cf60SHans Petter Selasky #endif
4189d40cf60SHans Petter Selasky #ifdef INET6
4199d40cf60SHans Petter Selasky 	struct sockaddr_in6 *sin6;
4209d40cf60SHans Petter Selasky #endif
4219d40cf60SHans Petter Selasky 	uint8_t *e_addr;
4229d40cf60SHans Petter Selasky 
4239d40cf60SHans Petter Selasky 	switch (sa->sa_family) {
4249d40cf60SHans Petter Selasky 	case AF_LINK:
4259d40cf60SHans Petter Selasky 		/*
4269d40cf60SHans Petter Selasky 		 * No mapping needed. Just check that it's a valid MC address.
4279d40cf60SHans Petter Selasky 		 */
4289d40cf60SHans Petter Selasky 		sdl = (struct sockaddr_dl *)sa;
4299d40cf60SHans Petter Selasky 		e_addr = LLADDR(sdl);
4309d40cf60SHans Petter Selasky 		if (!INFINIBAND_IS_MULTICAST(e_addr))
4319d40cf60SHans Petter Selasky 			return (EADDRNOTAVAIL);
4329d40cf60SHans Petter Selasky 		*llsa = NULL;
4339d40cf60SHans Petter Selasky 		return 0;
4349d40cf60SHans Petter Selasky 
4359d40cf60SHans Petter Selasky #ifdef INET
4369d40cf60SHans Petter Selasky 	case AF_INET:
4379d40cf60SHans Petter Selasky 		sin = (struct sockaddr_in *)sa;
4389d40cf60SHans Petter Selasky 		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
4399d40cf60SHans Petter Selasky 			return (EADDRNOTAVAIL);
4409d40cf60SHans Petter Selasky 		sdl = link_init_sdl(ifp, *llsa, IFT_INFINIBAND);
4419d40cf60SHans Petter Selasky 		sdl->sdl_alen = INFINIBAND_ADDR_LEN;
4429d40cf60SHans Petter Selasky 		e_addr = LLADDR(sdl);
44301630a49SHans Petter Selasky 		infiniband_ipv4_multicast_map(
44401630a49SHans Petter Selasky 		    sin->sin_addr.s_addr, ifp->if_broadcastaddr, e_addr);
4459d40cf60SHans Petter Selasky 		*llsa = (struct sockaddr *)sdl;
4469d40cf60SHans Petter Selasky 		return (0);
4479d40cf60SHans Petter Selasky #endif
4489d40cf60SHans Petter Selasky #ifdef INET6
4499d40cf60SHans Petter Selasky 	case AF_INET6:
4509d40cf60SHans Petter Selasky 		sin6 = (struct sockaddr_in6 *)sa;
4519d40cf60SHans Petter Selasky 		/*
4529d40cf60SHans Petter Selasky 		 * An IP6 address of 0 means listen to all of the
4539d40cf60SHans Petter Selasky 		 * multicast address used for IP6. This has no meaning
4549d40cf60SHans Petter Selasky 		 * in infiniband.
4559d40cf60SHans Petter Selasky 		 */
4569d40cf60SHans Petter Selasky 		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
4579d40cf60SHans Petter Selasky 			return (EADDRNOTAVAIL);
4589d40cf60SHans Petter Selasky 		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
4599d40cf60SHans Petter Selasky 			return (EADDRNOTAVAIL);
4609d40cf60SHans Petter Selasky 		sdl = link_init_sdl(ifp, *llsa, IFT_INFINIBAND);
4619d40cf60SHans Petter Selasky 		sdl->sdl_alen = INFINIBAND_ADDR_LEN;
4629d40cf60SHans Petter Selasky 		e_addr = LLADDR(sdl);
46301630a49SHans Petter Selasky 		infiniband_ipv6_multicast_map(
46401630a49SHans Petter Selasky 		    &sin6->sin6_addr, ifp->if_broadcastaddr, e_addr);
4659d40cf60SHans Petter Selasky 		*llsa = (struct sockaddr *)sdl;
4669d40cf60SHans Petter Selasky 		return (0);
4679d40cf60SHans Petter Selasky #endif
4689d40cf60SHans Petter Selasky 	default:
4699d40cf60SHans Petter Selasky 		return (EAFNOSUPPORT);
4709d40cf60SHans Petter Selasky 	}
4719d40cf60SHans Petter Selasky }
4729d40cf60SHans Petter Selasky 
4739d40cf60SHans Petter Selasky void
4749d40cf60SHans Petter Selasky infiniband_ifattach(struct ifnet *ifp, const uint8_t *lla, const uint8_t *llb)
4759d40cf60SHans Petter Selasky {
4769d40cf60SHans Petter Selasky 	struct sockaddr_dl *sdl;
4779d40cf60SHans Petter Selasky 	struct ifaddr *ifa;
4789d40cf60SHans Petter Selasky 	int i;
4799d40cf60SHans Petter Selasky 
4809d40cf60SHans Petter Selasky 	ifp->if_addrlen = INFINIBAND_ADDR_LEN;
4819d40cf60SHans Petter Selasky 	ifp->if_hdrlen = INFINIBAND_HDR_LEN;
4829d40cf60SHans Petter Selasky 	ifp->if_mtu = INFINIBAND_MTU;
4839d40cf60SHans Petter Selasky 	if_attach(ifp);
4849d40cf60SHans Petter Selasky 	ifp->if_output = infiniband_output;
4859d40cf60SHans Petter Selasky 	ifp->if_input = infiniband_input;
4869d40cf60SHans Petter Selasky 	ifp->if_resolvemulti = infiniband_resolvemulti;
4879d40cf60SHans Petter Selasky 
4889d40cf60SHans Petter Selasky 	if (ifp->if_baudrate == 0)
4899d40cf60SHans Petter Selasky 		ifp->if_baudrate = IF_Gbps(10); /* default value */
4909d40cf60SHans Petter Selasky 	if (llb != NULL)
4919d40cf60SHans Petter Selasky 		ifp->if_broadcastaddr = llb;
4929d40cf60SHans Petter Selasky 
4939d40cf60SHans Petter Selasky 	ifa = ifp->if_addr;
4949d40cf60SHans Petter Selasky 	KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
4959d40cf60SHans Petter Selasky 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
4969d40cf60SHans Petter Selasky 	sdl->sdl_type = IFT_INFINIBAND;
4979d40cf60SHans Petter Selasky 	sdl->sdl_alen = ifp->if_addrlen;
4989d40cf60SHans Petter Selasky 
4999d40cf60SHans Petter Selasky 	if (lla != NULL) {
5009d40cf60SHans Petter Selasky 		memcpy(LLADDR(sdl), lla, ifp->if_addrlen);
5019d40cf60SHans Petter Selasky 
5029d40cf60SHans Petter Selasky 		if (ifp->if_hw_addr != NULL)
5039d40cf60SHans Petter Selasky 			memcpy(ifp->if_hw_addr, lla, ifp->if_addrlen);
5049d40cf60SHans Petter Selasky 	} else {
5059d40cf60SHans Petter Selasky 		lla = LLADDR(sdl);
5069d40cf60SHans Petter Selasky 	}
5079d40cf60SHans Petter Selasky 
5089d40cf60SHans Petter Selasky 	/* Attach ethernet compatible network device */
5099d40cf60SHans Petter Selasky 	bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN);
5109d40cf60SHans Petter Selasky 
5119d40cf60SHans Petter Selasky 	/* Announce Infiniband MAC address if non-zero. */
5129d40cf60SHans Petter Selasky 	for (i = 0; i < ifp->if_addrlen; i++)
5139d40cf60SHans Petter Selasky 		if (lla[i] != 0)
5149d40cf60SHans Petter Selasky 			break;
5159d40cf60SHans Petter Selasky 	if (i != ifp->if_addrlen)
5169d40cf60SHans Petter Selasky 		if_printf(ifp, "Infiniband address: %20D\n", lla, ":");
5179d40cf60SHans Petter Selasky 
5189d40cf60SHans Petter Selasky 	/* Add necessary bits are setup; announce it now. */
5199d40cf60SHans Petter Selasky 	EVENTHANDLER_INVOKE(infiniband_ifattach_event, ifp);
5209d40cf60SHans Petter Selasky 
5219d40cf60SHans Petter Selasky 	if (IS_DEFAULT_VNET(curvnet))
5229d40cf60SHans Petter Selasky 		devctl_notify("INFINIBAND", ifp->if_xname, "IFATTACH", NULL);
5239d40cf60SHans Petter Selasky }
5249d40cf60SHans Petter Selasky 
5259d40cf60SHans Petter Selasky /*
5269d40cf60SHans Petter Selasky  * Perform common duties while detaching an Infiniband interface
5279d40cf60SHans Petter Selasky  */
5289d40cf60SHans Petter Selasky void
5299d40cf60SHans Petter Selasky infiniband_ifdetach(struct ifnet *ifp)
5309d40cf60SHans Petter Selasky {
5319d40cf60SHans Petter Selasky 	bpfdetach(ifp);
5329d40cf60SHans Petter Selasky 	if_detach(ifp);
5339d40cf60SHans Petter Selasky }
5349d40cf60SHans Petter Selasky 
5359d40cf60SHans Petter Selasky static int
5369d40cf60SHans Petter Selasky infiniband_modevent(module_t mod, int type, void *data)
5379d40cf60SHans Petter Selasky {
5389d40cf60SHans Petter Selasky 	switch (type) {
5399d40cf60SHans Petter Selasky 	case MOD_LOAD:
5409d40cf60SHans Petter Selasky 	case MOD_UNLOAD:
5419d40cf60SHans Petter Selasky 		return (0);
5429d40cf60SHans Petter Selasky 	default:
5439d40cf60SHans Petter Selasky 		return (EOPNOTSUPP);
5449d40cf60SHans Petter Selasky 	}
5459d40cf60SHans Petter Selasky }
5469d40cf60SHans Petter Selasky 
5479d40cf60SHans Petter Selasky static moduledata_t infiniband_mod = {
5489d40cf60SHans Petter Selasky 	.name = "if_infiniband",
5499d40cf60SHans Petter Selasky 	.evhand = &infiniband_modevent,
5509d40cf60SHans Petter Selasky };
5519d40cf60SHans Petter Selasky 
5529d40cf60SHans Petter Selasky DECLARE_MODULE(if_infiniband, infiniband_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
5539d40cf60SHans Petter Selasky MODULE_VERSION(if_infiniband, 1);
554