1139823Simp/*-
21541Srgrimes * Copyright (c) 1982, 1986, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes * 4. Neither the name of the University nor the names of its contributors
141541Srgrimes *    may be used to endorse or promote products derived from this software
151541Srgrimes *    without specific prior written permission.
161541Srgrimes *
171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271541Srgrimes * SUCH DAMAGE.
281541Srgrimes *
2985051Sru *	@(#)if_loop.c	8.2 (Berkeley) 1/9/95
3050477Speter * $FreeBSD$
311541Srgrimes */
321541Srgrimes
331541Srgrimes/*
341541Srgrimes * Loopback interface driver for protocol testing and timing.
351541Srgrimes */
361541Srgrimes
3732356Seivind#include "opt_atalk.h"
3832350Seivind#include "opt_inet.h"
3954263Sshin#include "opt_inet6.h"
4031742Seivind#include "opt_ipx.h"
4131742Seivind
421541Srgrimes#include <sys/param.h>
431541Srgrimes#include <sys/systm.h>
441541Srgrimes#include <sys/kernel.h>
451541Srgrimes#include <sys/mbuf.h>
4671862Speter#include <sys/module.h>
4791648Sbrooks#include <machine/bus.h>
4891648Sbrooks#include <sys/rman.h>
491541Srgrimes#include <sys/socket.h>
5024204Sbde#include <sys/sockio.h>
5171791Speter#include <sys/sysctl.h>
521541Srgrimes
531541Srgrimes#include <net/if.h>
54130933Sbrooks#include <net/if_clone.h>
551541Srgrimes#include <net/if_types.h>
561541Srgrimes#include <net/netisr.h>
571541Srgrimes#include <net/route.h>
581541Srgrimes#include <net/bpf.h>
59185571Sbz#include <net/vnet.h>
601541Srgrimes
611541Srgrimes#ifdef	INET
621541Srgrimes#include <netinet/in.h>
631541Srgrimes#include <netinet/in_var.h>
641541Srgrimes#endif
651541Srgrimes
6611819Sjulian#ifdef IPX
6711819Sjulian#include <netipx/ipx.h>
6811819Sjulian#include <netipx/ipx_if.h>
6911819Sjulian#endif
7011819Sjulian
7153541Sshin#ifdef INET6
7253541Sshin#ifndef INET
7353541Sshin#include <netinet/in.h>
7453541Sshin#endif
7553541Sshin#include <netinet6/in6_var.h>
7662587Sitojun#include <netinet/ip6.h>
7753541Sshin#endif
7853541Sshin
7915885Sjulian#ifdef NETATALK
8015885Sjulian#include <netatalk/at.h>
8115885Sjulian#include <netatalk/at_var.h>
8283268Speter#endif
8315885Sjulian
84187039Srwatson#include <security/mac/mac_framework.h>
85187039Srwatson
861622Sdg#ifdef TINY_LOMTU
871541Srgrimes#define	LOMTU	(1024+512)
8853541Sshin#elif defined(LARGE_LOMTU)
8953541Sshin#define LOMTU	131072
901622Sdg#else
916876Sdg#define LOMTU	16384
921622Sdg#endif
931541Srgrimes
94189873Srwatson#define	LO_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP)
95236332Stuexen#define	LO_CSUM_FEATURES6	(CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | CSUM_SCTP_IPV6)
96236170Sbz#define	LO_CSUM_SET		(CSUM_DATA_VALID | CSUM_DATA_VALID_IPV6 | \
97236170Sbz				    CSUM_PSEUDO_HDR | \
98189873Srwatson				    CSUM_IP_CHECKED | CSUM_IP_VALID | \
99189873Srwatson				    CSUM_SCTP_VALID)
100189873Srwatson
10191648Sbrooksint		loioctl(struct ifnet *, u_long, caddr_t);
10291648Sbrooksstatic void	lortrequest(int, struct rtentry *, struct rt_addrinfo *);
10391648Sbrooksint		looutput(struct ifnet *ifp, struct mbuf *m,
104249925Sglebius		    const struct sockaddr *dst, struct route *ro);
105160195Ssamstatic int	lo_clone_create(struct if_clone *, int, caddr_t);
106128209Sbrooksstatic void	lo_clone_destroy(struct ifnet *);
10791648Sbrooks
108195699SrwatsonVNET_DEFINE(struct ifnet *, loif);	/* Used externally */
10991648Sbrooks
110192669Szec#ifdef VIMAGE
111241610Sglebiusstatic VNET_DEFINE(struct if_clone *, lo_cloner);
112195727Srwatson#define	V_lo_cloner		VNET(lo_cloner)
113192669Szec#endif
114192669Szec
115241610Sglebiusstatic struct if_clone *lo_cloner;
116241610Sglebiusstatic const char loname[] = "lo";
11791648Sbrooks
118128209Sbrooksstatic void
119177965Srwatsonlo_clone_destroy(struct ifnet *ifp)
12091648Sbrooks{
12191648Sbrooks
122193731Szec#ifndef VIMAGE
12397289Sbrooks	/* XXX: destroying lo0 will lead to panics. */
124181803Sbz	KASSERT(V_loif != ifp, ("%s: destroying lo0", __func__));
125193731Szec#endif
12691648Sbrooks
12791648Sbrooks	bpfdetach(ifp);
12891648Sbrooks	if_detach(ifp);
129147256Sbrooks	if_free(ifp);
13091648Sbrooks}
13191648Sbrooks
132128209Sbrooksstatic int
133177965Srwatsonlo_clone_create(struct if_clone *ifc, int unit, caddr_t params)
13471791Speter{
135147256Sbrooks	struct ifnet *ifp;
13671791Speter
137180094Sed	ifp = if_alloc(IFT_LOOP);
138180094Sed	if (ifp == NULL)
139147256Sbrooks		return (ENOSPC);
14071791Speter
141241610Sglebius	if_initname(ifp, loname, unit);
142147256Sbrooks	ifp->if_mtu = LOMTU;
143147256Sbrooks	ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST;
144147256Sbrooks	ifp->if_ioctl = loioctl;
145147256Sbrooks	ifp->if_output = looutput;
146147256Sbrooks	ifp->if_snd.ifq_maxlen = ifqmaxlen;
147236170Sbz	ifp->if_capabilities = ifp->if_capenable =
148236170Sbz	    IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6;
149236170Sbz	ifp->if_hwassist = LO_CSUM_FEATURES | LO_CSUM_FEATURES6;
150147256Sbrooks	if_attach(ifp);
151147611Sdwmalone	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
152181803Sbz	if (V_loif == NULL)
153181803Sbz		V_loif = ifp;
15492081Smux
15592081Smux	return (0);
15671791Speter}
15771791Speter
158195837Srwatsonstatic void
159195837Srwatsonvnet_loif_init(const void *unused __unused)
160190787Szec{
161190787Szec
162192669Szec#ifdef VIMAGE
163241610Sglebius	lo_cloner = if_clone_simple(loname, lo_clone_create, lo_clone_destroy,
164241610Sglebius	    1);
165195837Srwatson	V_lo_cloner = lo_cloner;
166192669Szec#else
167241610Sglebius	lo_cloner = if_clone_simple(loname, lo_clone_create, lo_clone_destroy,
168241610Sglebius	    1);
169192669Szec#endif
170190787Szec}
171195837SrwatsonVNET_SYSINIT(vnet_loif_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
172195837Srwatson    vnet_loif_init, NULL);
173190787Szec
174193731Szec#ifdef VIMAGE
175195837Srwatsonstatic void
176195837Srwatsonvnet_loif_uninit(const void *unused __unused)
177193731Szec{
178193731Szec
179241610Sglebius	if_clone_detach(V_lo_cloner);
180193731Szec	V_loif = NULL;
181193731Szec}
182195837SrwatsonVNET_SYSUNINIT(vnet_loif_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
183195837Srwatson    vnet_loif_uninit, NULL);
184193731Szec#endif
185193731Szec
186193731Szecstatic int
187178883Srwatsonloop_modevent(module_t mod, int type, void *data)
188178883Srwatson{
189177965Srwatson
190178883Srwatson	switch (type) {
191178883Srwatson	case MOD_LOAD:
192178883Srwatson		break;
193177965Srwatson
194178883Srwatson	case MOD_UNLOAD:
195178883Srwatson		printf("loop module unload - not possible for this module type\n");
196177965Srwatson		return (EINVAL);
197177965Srwatson
198132199Sphk	default:
199177965Srwatson		return (EOPNOTSUPP);
200178883Srwatson	}
201177965Srwatson	return (0);
202178883Srwatson}
2031541Srgrimes
204178883Srwatsonstatic moduledata_t loop_mod = {
205204173Srwatson	"if_lo",
206178883Srwatson	loop_modevent,
207241394Skevlo	0
208178883Srwatson};
20971862Speter
210204173SrwatsonDECLARE_MODULE(if_lo, loop_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
21171862Speter
21254263Sshinint
213249925Sglebiuslooutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
214191148Skmacy    struct route *ro)
2151541Srgrimes{
216147611Sdwmalone	u_int32_t af;
217191148Skmacy	struct rtentry *rt = NULL;
218187039Srwatson#ifdef MAC
219187039Srwatson	int error;
220187039Srwatson#endif
221147611Sdwmalone
222113255Sdes	M_ASSERTPKTHDR(m); /* check if we have the packet header */
223113255Sdes
224191148Skmacy	if (ro != NULL)
225191148Skmacy		rt = ro->ro_rt;
226187039Srwatson#ifdef MAC
227187039Srwatson	error = mac_ifnet_check_transmit(ifp, m);
228187039Srwatson	if (error) {
229187039Srwatson		m_freem(m);
230187039Srwatson		return (error);
231187039Srwatson	}
232187039Srwatson#endif
233187039Srwatson
23436908Sjulian	if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
23536908Sjulian		m_freem(m);
23636908Sjulian		return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
23736908Sjulian		        rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
23836908Sjulian	}
23953541Sshin
24036908Sjulian	ifp->if_opackets++;
24136908Sjulian	ifp->if_obytes += m->m_pkthdr.len;
242147611Sdwmalone
243147611Sdwmalone	/* BPF writes need to be handled specially. */
244249925Sglebius	if (dst->sa_family == AF_UNSPEC)
245147611Sdwmalone		bcopy(dst->sa_data, &af, sizeof(af));
246249925Sglebius	else
247249925Sglebius		af = dst->sa_family;
248147611Sdwmalone
24936992Sjulian#if 1	/* XXX */
250249925Sglebius	switch (af) {
25136992Sjulian	case AF_INET:
252189863Srwatson		if (ifp->if_capenable & IFCAP_RXCSUM) {
253189863Srwatson			m->m_pkthdr.csum_data = 0xffff;
254189873Srwatson			m->m_pkthdr.csum_flags = LO_CSUM_SET;
255189863Srwatson		}
256189873Srwatson		m->m_pkthdr.csum_flags &= ~LO_CSUM_FEATURES;
257236170Sbz		break;
258236170Sbz	case AF_INET6:
259238871Sbz#if 0
260238871Sbz		/*
261238871Sbz		 * XXX-BZ for now always claim the checksum is good despite
262238871Sbz		 * any interface flags.   This is a workaround for 9.1-R and
263238871Sbz		 * a proper solution ought to be sought later.
264238871Sbz		 */
265236170Sbz		if (ifp->if_capenable & IFCAP_RXCSUM_IPV6) {
266236170Sbz			m->m_pkthdr.csum_data = 0xffff;
267236170Sbz			m->m_pkthdr.csum_flags = LO_CSUM_SET;
268236170Sbz		}
269238871Sbz#else
270238871Sbz		m->m_pkthdr.csum_data = 0xffff;
271238871Sbz		m->m_pkthdr.csum_flags = LO_CSUM_SET;
272238871Sbz#endif
273236170Sbz		m->m_pkthdr.csum_flags &= ~LO_CSUM_FEATURES6;
274236170Sbz		break;
27536992Sjulian	case AF_IPX:
27636992Sjulian	case AF_APPLETALK:
27736994Sjulian		break;
27836992Sjulian	default:
279249925Sglebius		printf("looutput: af=%d unexpected\n", af);
28036992Sjulian		m_freem(m);
28136992Sjulian		return (EAFNOSUPPORT);
28236992Sjulian	}
28336992Sjulian#endif
284249925Sglebius	return (if_simloop(ifp, m, af, 0));
28536908Sjulian}
28636908Sjulian
28736908Sjulian/*
28836908Sjulian * if_simloop()
28936908Sjulian *
29036908Sjulian * This function is to support software emulation of hardware loopback,
29136908Sjulian * i.e., for interfaces with the IFF_SIMPLEX attribute. Since they can't
29236908Sjulian * hear their own broadcasts, we create a copy of the packet that we
29336908Sjulian * would normally receive via a hardware loopback.
29436908Sjulian *
29536908Sjulian * This function expects the packet to include the media header of length hlen.
29636908Sjulian */
29736908Sjulianint
298177965Srwatsonif_simloop(struct ifnet *ifp, struct mbuf *m, int af, int hlen)
29936908Sjulian{
30069152Sjlemon	int isr;
3011541Srgrimes
302113255Sdes	M_ASSERTPKTHDR(m);
303121645Ssam	m_tag_delete_nonpersistent(m);
30436908Sjulian	m->m_pkthdr.rcvif = ifp;
30560889Sarchie
306187039Srwatson#ifdef MAC
307187039Srwatson	mac_ifnet_create_mbuf(ifp, m);
308187039Srwatson#endif
309187039Srwatson
310162539Ssuz	/*
311162539Ssuz	 * Let BPF see incoming packet in the following manner:
312178883Srwatson	 *  - Emulated packet loopback for a simplex interface
313162539Ssuz	 *    (net/if_ethersubr.c)
314162539Ssuz	 *	-> passes it to ifp's BPF
315162539Ssuz	 *  - IPv4/v6 multicast packet loopback (netinet(6)/ip(6)_output.c)
316162539Ssuz	 *	-> not passes it to any BPF
317162539Ssuz	 *  - Normal packet loopback from myself to myself (net/if_loop.c)
318162539Ssuz	 *	-> passes to lo0's BPF (even in case of IPv6, where ifp!=lo0)
319162539Ssuz	 */
320162539Ssuz	if (hlen > 0) {
321162539Ssuz		if (bpf_peers_present(ifp->if_bpf)) {
322123922Ssam			bpf_mtap(ifp->if_bpf, m);
323162539Ssuz		}
324162539Ssuz	} else {
325181803Sbz		if (bpf_peers_present(V_loif->if_bpf)) {
326181803Sbz			if ((m->m_flags & M_MCAST) == 0 || V_loif == ifp) {
327162539Ssuz				/* XXX beware sizeof(af) != 4 */
328181118Srwatson				u_int32_t af1 = af;
329162539Ssuz
330162539Ssuz				/*
331162539Ssuz				 * We need to prepend the address family.
332162539Ssuz				 */
333181803Sbz				bpf_mtap2(V_loif->if_bpf, &af1, sizeof(af1), m);
334162539Ssuz			}
335162539Ssuz		}
3361541Srgrimes	}
3371541Srgrimes
33836908Sjulian	/* Strip away media header */
33937600Sdfr	if (hlen > 0) {
34060952Sgallatin		m_adj(m, hlen);
341166577Scognet#ifndef __NO_STRICT_ALIGNMENT
342158471Sjhb		/*
343158471Sjhb		 * Some archs do not like unaligned data, so
344158471Sjhb		 * we move data down in the first mbuf.
345158471Sjhb		 */
34660952Sgallatin		if (mtod(m, vm_offset_t) & 3) {
34761181Smjacob			KASSERT(hlen >= 3, ("if_simloop: hlen too small"));
348178883Srwatson			bcopy(m->m_data,
349178883Srwatson			    (char *)(mtod(m, vm_offset_t)
35060952Sgallatin				- (mtod(m, vm_offset_t) & 3)),
35160952Sgallatin			    m->m_len);
352132780Skan			m->m_data -= (mtod(m,vm_offset_t) & 3);
35360952Sgallatin		}
35437600Sdfr#endif
35537600Sdfr	}
35636908Sjulian
35760889Sarchie	/* Deliver to upper layer protocol */
35860889Sarchie	switch (af) {
3591541Srgrimes#ifdef INET
3601541Srgrimes	case AF_INET:
3611541Srgrimes		isr = NETISR_IP;
3621541Srgrimes		break;
3631541Srgrimes#endif
36453541Sshin#ifdef INET6
36553541Sshin	case AF_INET6:
36653541Sshin		m->m_flags |= M_LOOP;
36753541Sshin		isr = NETISR_IPV6;
36853541Sshin		break;
36953541Sshin#endif
37011819Sjulian#ifdef IPX
37111819Sjulian	case AF_IPX:
37211819Sjulian		isr = NETISR_IPX;
37311819Sjulian		break;
37411819Sjulian#endif
37515885Sjulian#ifdef NETATALK
37615885Sjulian	case AF_APPLETALK:
377111888Sjlemon		isr = NETISR_ATALK2;
37815885Sjulian		break;
37983268Speter#endif
3801541Srgrimes	default:
38160889Sarchie		printf("if_simloop: can't handle af=%d\n", af);
3821541Srgrimes		m_freem(m);
3831541Srgrimes		return (EAFNOSUPPORT);
3841541Srgrimes	}
3851541Srgrimes	ifp->if_ipackets++;
3861541Srgrimes	ifp->if_ibytes += m->m_pkthdr.len;
387134391Sandre	netisr_queue(isr, m);	/* mbuf is free'd on failure. */
3881541Srgrimes	return (0);
3891541Srgrimes}
3901541Srgrimes
3911541Srgrimes/* ARGSUSED */
39212706Sphkstatic void
393177965Srwatsonlortrequest(int cmd, struct rtentry *rt, struct rt_addrinfo *info)
3941541Srgrimes{
395177965Srwatson
396120727Ssam	RT_LOCK_ASSERT(rt);
397263478Sglebius	rt->rt_mtu = rt->rt_ifp->if_mtu;
3981541Srgrimes}
3991541Srgrimes
4001541Srgrimes/*
4011541Srgrimes * Process an ioctl request.
4021541Srgrimes */
4031541Srgrimes/* ARGSUSED */
40454263Sshinint
405177965Srwatsonloioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
4061541Srgrimes{
407177965Srwatson	struct ifaddr *ifa;
408177965Srwatson	struct ifreq *ifr = (struct ifreq *)data;
409189863Srwatson	int error = 0, mask;
4101541Srgrimes
4111541Srgrimes	switch (cmd) {
4121541Srgrimes	case SIOCSIFADDR:
413148887Srwatson		ifp->if_flags |= IFF_UP;
414148887Srwatson		ifp->if_drv_flags |= IFF_DRV_RUNNING;
4151541Srgrimes		ifa = (struct ifaddr *)data;
41613928Swollman		ifa->ifa_rtrequest = lortrequest;
4171541Srgrimes		/*
4181541Srgrimes		 * Everything else is done at a higher level.
4191541Srgrimes		 */
4201541Srgrimes		break;
4211541Srgrimes
4221541Srgrimes	case SIOCADDMULTI:
4231541Srgrimes	case SIOCDELMULTI:
4241541Srgrimes		if (ifr == 0) {
4251541Srgrimes			error = EAFNOSUPPORT;		/* XXX */
4261541Srgrimes			break;
4271541Srgrimes		}
4281541Srgrimes		switch (ifr->ifr_addr.sa_family) {
4291541Srgrimes
4301541Srgrimes#ifdef INET
4311541Srgrimes		case AF_INET:
4321541Srgrimes			break;
4331541Srgrimes#endif
43453541Sshin#ifdef INET6
43553541Sshin		case AF_INET6:
43653541Sshin			break;
43753541Sshin#endif
4381541Srgrimes
4391541Srgrimes		default:
4401541Srgrimes			error = EAFNOSUPPORT;
4411541Srgrimes			break;
4421541Srgrimes		}
4431541Srgrimes		break;
4441541Srgrimes
4451944Sdg	case SIOCSIFMTU:
44649468Sbrian		ifp->if_mtu = ifr->ifr_mtu;
4471944Sdg		break;
4481944Sdg
44935563Sphk	case SIOCSIFFLAGS:
45035563Sphk		break;
45135563Sphk
452189863Srwatson	case SIOCSIFCAP:
453189863Srwatson		mask = ifp->if_capenable ^ ifr->ifr_reqcap;
454189863Srwatson		if ((mask & IFCAP_RXCSUM) != 0)
455189863Srwatson			ifp->if_capenable ^= IFCAP_RXCSUM;
456189863Srwatson		if ((mask & IFCAP_TXCSUM) != 0)
457189863Srwatson			ifp->if_capenable ^= IFCAP_TXCSUM;
458238871Sbz		if ((mask & IFCAP_RXCSUM_IPV6) != 0) {
459238871Sbz#if 0
460236170Sbz			ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
461238871Sbz#else
462238871Sbz			error = EOPNOTSUPP;
463238871Sbz			break;
464238871Sbz#endif
465238871Sbz		}
466238871Sbz		if ((mask & IFCAP_TXCSUM_IPV6) != 0) {
467238871Sbz#if 0
468236170Sbz			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
469238871Sbz#else
470238871Sbz			error = EOPNOTSUPP;
471238871Sbz			break;
472238871Sbz#endif
473238871Sbz		}
474236170Sbz		ifp->if_hwassist = 0;
475189863Srwatson		if (ifp->if_capenable & IFCAP_TXCSUM)
476189873Srwatson			ifp->if_hwassist = LO_CSUM_FEATURES;
477238871Sbz#if 0
478236170Sbz		if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
479236170Sbz			ifp->if_hwassist |= LO_CSUM_FEATURES6;
480238871Sbz#endif
481189863Srwatson		break;
482189863Srwatson
4831541Srgrimes	default:
4841541Srgrimes		error = EINVAL;
4851541Srgrimes	}
4861541Srgrimes	return (error);
4871541Srgrimes}
488