implement ipv6 TSO.

partly from Matthias Scheler.  tested by him.
This commit is contained in:
yamt 2006-11-23 19:41:58 +00:00
parent 22b629efe2
commit 809ec70bcf
8 changed files with 338 additions and 52 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ifconfig.8,v 1.86 2006/09/16 20:15:49 hubertf Exp $
.\" $NetBSD: ifconfig.8,v 1.87 2006/11/23 19:43:52 yamt Exp $
.\"
.\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94
.\"
.Dd April 29, 2006
.Dd November 24, 2006
.Dt IFCONFIG 8
.Os
.Sh NAME
@ -675,6 +675,12 @@ support it.
.It Cm -tso4
Disable hardware-assisted TCP/IPv4 segmentation on interfaces that
support it.
.It Cm tso6
Enable hardware-assisted TCP/IPv6 segmentation on interfaces that
support it.
.It Cm -tso6
Disable hardware-assisted TCP/IPv6 segmentation on interfaces that
support it.
.El
.Pp
.Nm

View File

@ -1,4 +1,4 @@
/* $NetBSD: ifconfig.c,v 1.178 2006/11/13 05:13:39 dyoung Exp $ */
/* $NetBSD: ifconfig.c,v 1.179 2006/11/23 19:43:52 yamt Exp $ */
/*-
* Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
@ -76,7 +76,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
#if 0
static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94";
#else
__RCSID("$NetBSD: ifconfig.c,v 1.178 2006/11/13 05:13:39 dyoung Exp $");
__RCSID("$NetBSD: ifconfig.c,v 1.179 2006/11/23 19:43:52 yamt Exp $");
#endif
#endif /* not lint */
@ -329,6 +329,8 @@ const struct cmd {
0, setifcaps },
{ "tso4", IFCAP_TSOv4, 0, setifcaps },
{ "-tso4", -IFCAP_TSOv4, 0, setifcaps },
{ "tso6", IFCAP_TSOv6, 0, setifcaps },
{ "-tso6", -IFCAP_TSOv6, 0, setifcaps },
{ "agrport", NEXTARG, 0, agraddport } ,
{ "-agrport", NEXTARG, 0, agrremport } ,
{ 0, 0, 0, setifaddr },

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_wm.c,v 1.130 2006/11/16 06:07:54 yamt Exp $ */
/* $NetBSD: if_wm.c,v 1.131 2006/11/23 19:42:59 yamt Exp $ */
/*
* Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@ -47,7 +47,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.130 2006/11/16 06:07:54 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.131 2006/11/23 19:42:59 yamt Exp $");
#include "bpfilter.h"
#include "rnd.h"
@ -83,6 +83,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.130 2006/11/16 06:07:54 yamt Exp $");
#include <netinet/in.h> /* XXX for struct ip */
#include <netinet/in_systm.h> /* XXX for struct ip */
#include <netinet/ip.h> /* XXX for struct ip */
#include <netinet/ip6.h> /* XXX for struct ip6_hdr */
#include <netinet/tcp.h> /* XXX for struct tcphdr */
#include <machine/bus.h>
@ -286,7 +287,8 @@ struct wm_softc {
struct evcnt sc_ev_txipsum; /* IP checksums comp. out-bound */
struct evcnt sc_ev_txtusum; /* TCP/UDP cksums comp. out-bound */
struct evcnt sc_ev_txtusum6; /* TCP/UDP v6 cksums comp. out-bound */
struct evcnt sc_ev_txtso; /* TCP seg offload out-bound */
struct evcnt sc_ev_txtso; /* TCP seg offload out-bound (IPv4) */
struct evcnt sc_ev_txtso6; /* TCP seg offload out-bound (IPv6) */
struct evcnt sc_ev_txtsopain; /* painful header manip. for TSO */
struct evcnt sc_ev_txseg[WM_NTXSEGS]; /* Tx packets w/ N segments */
@ -1419,8 +1421,13 @@ wm_attach(struct device *parent, struct device *self, void *aux)
* If we're a i82544 or greater (except i82547), we can do
* TCP segmentation offload.
*/
if (sc->sc_type >= WM_T_82544 && sc->sc_type != WM_T_82547)
if (sc->sc_type >= WM_T_82544 && sc->sc_type != WM_T_82547) {
ifp->if_capabilities |= IFCAP_TSOv4;
}
if (sc->sc_type >= WM_T_82571) {
ifp->if_capabilities |= IFCAP_TSOv6;
}
/*
* Attach the interface.
@ -1462,6 +1469,8 @@ wm_attach(struct device *parent, struct device *self, void *aux)
evcnt_attach_dynamic(&sc->sc_ev_txtso, EVCNT_TYPE_MISC,
NULL, sc->sc_dev.dv_xname, "txtso");
evcnt_attach_dynamic(&sc->sc_ev_txtso6, EVCNT_TYPE_MISC,
NULL, sc->sc_dev.dv_xname, "txtso6");
evcnt_attach_dynamic(&sc->sc_ev_txtsopain, EVCNT_TYPE_MISC,
NULL, sc->sc_dev.dv_xname, "txtsopain");
@ -1587,6 +1596,7 @@ wm_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, uint32_t *cmdp,
struct mbuf *m0 = txs->txs_mbuf;
struct livengood_tcpip_ctxdesc *t;
uint32_t ipcs, tucs, cmd, cmdlen, seg;
uint32_t ipcse;
struct ether_header *eh;
int offset, iphl;
uint8_t fields;
@ -1622,15 +1632,17 @@ wm_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, uint32_t *cmdp,
} else {
iphl = M_CSUM_DATA_IPv6_HL(m0->m_pkthdr.csum_data);
}
ipcse = offset + iphl - 1;
cmd = WTX_CMD_DEXT | WTX_DTYP_D;
cmdlen = WTX_CMD_DEXT | WTX_DTYP_C | WTX_CMD_IDE;
seg = 0;
fields = 0;
if (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) {
if ((m0->m_pkthdr.csum_flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) != 0) {
int hlen = offset + iphl;
WM_EVCNT_INCR(&sc->sc_ev_txtso);
boolean_t v4 = (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0;
if (__predict_false(m0->m_len <
(hlen + sizeof(struct tcphdr)))) {
/*
@ -1638,22 +1650,32 @@ wm_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, uint32_t *cmdp,
* to do this the slow and painful way. Let's just
* hope this doesn't happen very often.
*/
struct ip ip;
struct tcphdr th;
WM_EVCNT_INCR(&sc->sc_ev_txtsopain);
m_copydata(m0, offset, sizeof(ip), &ip);
m_copydata(m0, hlen, sizeof(th), &th);
if (v4) {
struct ip ip;
ip.ip_len = 0;
m_copyback(m0, offset + offsetof(struct ip, ip_len),
sizeof(ip.ip_len), &ip.ip_len);
th.th_sum = in_cksum_phdr(ip.ip_src.s_addr,
ip.ip_dst.s_addr, htons(IPPROTO_TCP));
m_copydata(m0, offset, sizeof(ip), &ip);
ip.ip_len = 0;
m_copyback(m0,
offset + offsetof(struct ip, ip_len),
sizeof(ip.ip_len), &ip.ip_len);
th.th_sum = in_cksum_phdr(ip.ip_src.s_addr,
ip.ip_dst.s_addr, htons(IPPROTO_TCP));
} else {
struct ip6_hdr ip6;
m_copydata(m0, offset, sizeof(ip6), &ip6);
ip6.ip6_plen = 0;
m_copyback(m0,
offset + offsetof(struct ip6_hdr, ip6_plen),
sizeof(ip6.ip6_plen), &ip6.ip6_plen);
th.th_sum = in6_cksum_phdr(&ip6.ip6_src,
&ip6.ip6_dst, 0, htonl(IPPROTO_TCP));
}
m_copyback(m0, hlen + offsetof(struct tcphdr, th_sum),
sizeof(th.th_sum), &th.th_sum);
@ -1663,20 +1685,37 @@ wm_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, uint32_t *cmdp,
* TCP/IP headers are in the first mbuf; we can do
* this the easy way.
*/
struct ip *ip =
(struct ip *) (mtod(m0, caddr_t) + offset);
struct tcphdr *th =
(struct tcphdr *) (mtod(m0, caddr_t) + hlen);
struct tcphdr *th;
ip->ip_len = 0;
th->th_sum = in_cksum_phdr(ip->ip_src.s_addr,
ip->ip_dst.s_addr, htons(IPPROTO_TCP));
if (v4) {
struct ip *ip =
(void *)(mtod(m0, caddr_t) + offset);
th = (void *)(mtod(m0, caddr_t) + hlen);
ip->ip_len = 0;
th->th_sum = in_cksum_phdr(ip->ip_src.s_addr,
ip->ip_dst.s_addr, htons(IPPROTO_TCP));
} else {
struct ip6_hdr *ip6 =
(void *)(mtod(m0, char *) + offset);
th = (void *)(mtod(m0, char *) + hlen);
ip6->ip6_plen = 0;
th->th_sum = in6_cksum_phdr(&ip6->ip6_src,
&ip6->ip6_dst, 0, htonl(IPPROTO_TCP));
}
hlen += th->th_off << 2;
}
if (v4) {
WM_EVCNT_INCR(&sc->sc_ev_txtso);
cmdlen |= WTX_TCPIP_CMD_IP;
} else {
WM_EVCNT_INCR(&sc->sc_ev_txtso6);
ipcse = 0;
}
cmd |= WTX_TCPIP_CMD_TSE;
cmdlen |= WTX_TCPIP_CMD_TSE | WTX_TCPIP_CMD_IP |
cmdlen |= WTX_TCPIP_CMD_TSE |
WTX_TCPIP_CMD_TCP | (m0->m_pkthdr.len - hlen);
seg = WTX_TCPIP_SEG_HDRLEN(hlen) |
WTX_TCPIP_SEG_MSS(m0->m_pkthdr.segsz);
@ -1690,7 +1729,7 @@ wm_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, uint32_t *cmdp,
ipcs = WTX_TCPIP_IPCSS(offset) |
WTX_TCPIP_IPCSO(offset + offsetof(struct ip, ip_sum)) |
WTX_TCPIP_IPCSE(offset + iphl - 1);
WTX_TCPIP_IPCSE(ipcse);
if (m0->m_pkthdr.csum_flags & (M_CSUM_IPv4|M_CSUM_TSOv4)) {
WM_EVCNT_INCR(&sc->sc_ev_txipsum);
fields |= WTX_IXSM;
@ -1707,7 +1746,7 @@ wm_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, uint32_t *cmdp,
M_CSUM_DATA_IPv4_OFFSET(m0->m_pkthdr.csum_data)) |
WTX_TCPIP_TUCSE(0) /* rest of packet */;
} else if ((m0->m_pkthdr.csum_flags &
(M_CSUM_TCPv6|M_CSUM_UDPv6)) != 0) {
(M_CSUM_TCPv6|M_CSUM_UDPv6|M_CSUM_TSOv6)) != 0) {
WM_EVCNT_INCR(&sc->sc_ev_txtusum6);
fields |= WTX_TXSM;
tucs = WTX_TCPIP_TUCSS(offset) |
@ -1904,7 +1943,8 @@ wm_start(struct ifnet *ifp)
txs = &sc->sc_txsoft[sc->sc_txsnext];
dmamap = txs->txs_dmamap;
use_tso = (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0;
use_tso = (m0->m_pkthdr.csum_flags &
(M_CSUM_TSOv4 | M_CSUM_TSOv6)) != 0;
/*
* So says the Linux driver:
@ -2024,7 +2064,8 @@ wm_start(struct ifnet *ifp)
/* Set up offload parameters for this packet. */
if (m0->m_pkthdr.csum_flags &
(M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4|
(M_CSUM_TSOv4|M_CSUM_TSOv6|
M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4|
M_CSUM_TCPv6|M_CSUM_UDPv6)) {
if (wm_tx_offload(sc, txs, &cksumcmd,
&cksumfields) != 0) {

View File

@ -1,4 +1,4 @@
/* $NetBSD: if.h,v 1.120 2006/11/13 05:13:40 dyoung Exp $ */
/* $NetBSD: if.h,v 1.121 2006/11/23 19:41:58 yamt Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@ -364,6 +364,7 @@ struct ifnet { /* and the entries */
#define IFCAP_CSUM_TCPv6_Tx 0x08000 /* can do IPv6/TCP checksums (Tx) */
#define IFCAP_CSUM_UDPv6_Rx 0x10000 /* can do IPv6/UDP checksums (Rx) */
#define IFCAP_CSUM_UDPv6_Tx 0x20000 /* can do IPv6/UDP checksums (Tx) */
#define IFCAP_TSOv6 0x40000 /* can do TCPv6 segmentation offload */
#define IFCAPBITS \
"\020" \
@ -377,7 +378,8 @@ struct ifnet { /* and the entries */
"\17TCP6CSUM_Rx" \
"\20TCP6CSUM_Tx" \
"\21UDP6CSUM_Rx" \
"\22UDP6CSUM_Tx"
"\22UDP6CSUM_Tx" \
"\23TSO6"
/*
* Output queues (ifp->if_snd) and internetwork datagram level (pup level 1)

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_output.c,v 1.150 2006/10/17 11:11:40 yamt Exp $ */
/* $NetBSD: tcp_output.c,v 1.151 2006/11/23 19:41:58 yamt Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -142,7 +142,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tcp_output.c,v 1.150 2006/10/17 11:11:40 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: tcp_output.c,v 1.151 2006/11/23 19:41:58 yamt Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
@ -200,6 +200,7 @@ __KERNEL_RCSID(0, "$NetBSD: tcp_output.c,v 1.150 2006/10/17 11:11:40 yamt Exp $"
#include <netinet/tcpip.h>
#include <netinet/tcp_debug.h>
#include <netinet/in_offload.h>
#include <netinet6/in6_offload.h>
#ifdef IPSEC
#include <netkey/key.h>
@ -239,7 +240,8 @@ static
inline
#endif
int
tcp_segsize(struct tcpcb *tp, int *txsegsizep, int *rxsegsizep)
tcp_segsize(struct tcpcb *tp, int *txsegsizep, int *rxsegsizep,
boolean_t *alwaysfragp)
{
#ifdef INET
struct inpcb *inp = tp->t_inpcb;
@ -254,6 +256,8 @@ tcp_segsize(struct tcpcb *tp, int *txsegsizep, int *rxsegsizep)
int hdrlen;
int optlen;
*alwaysfragp = FALSE;
#ifdef DIAGNOSTIC
if (tp->t_inpcb && tp->t_in6pcb)
panic("tcp_segsize: both t_inpcb and t_in6pcb are set");
@ -304,6 +308,7 @@ tcp_segsize(struct tcpcb *tp, int *txsegsizep, int *rxsegsizep)
* attach fragment header.
*/
size = IPV6_MMTU - hdrlen - sizeof(struct ip6_frag);
*alwaysfragp = TRUE;
} else
size = rt->rt_rmx.rmx_mtu - hdrlen;
#else
@ -558,7 +563,9 @@ tcp_output(struct tcpcb *tp)
int maxburst = TCP_MAXBURST;
int af; /* address family on the wire */
int iphdrlen;
int has_tso4, has_tso6;
int has_tso, use_tso;
boolean_t alwaysfrag;
int sack_rxmit;
int sack_bytes_rxmt;
struct sackhole *p;
@ -605,7 +612,7 @@ tcp_output(struct tcpcb *tp)
return (EAFNOSUPPORT);
}
if (tcp_segsize(tp, &txsegsize, &rxsegsize))
if (tcp_segsize(tp, &txsegsize, &rxsegsize, &alwaysfrag))
return (EMSGSIZE);
idle = (tp->snd_max == tp->snd_una);
@ -616,7 +623,9 @@ tcp_output(struct tcpcb *tp)
* - If there is not an IPsec policy that prevents it
* - If the interface can do it
*/
has_tso = tp->t_inpcb != NULL &&
has_tso4 = has_tso6 = FALSE;
#if defined(INET)
has_tso4 = tp->t_inpcb != NULL &&
#if defined(IPSEC) || defined(FAST_IPSEC)
IPSEC_PCB_SKIP_IPSEC(tp->t_inpcb->inp_sp,
IPSEC_DIR_OUTBOUND) &&
@ -624,6 +633,18 @@ tcp_output(struct tcpcb *tp)
tp->t_inpcb->inp_route.ro_rt != NULL &&
(tp->t_inpcb->inp_route.ro_rt->rt_ifp->if_capenable &
IFCAP_TSOv4) != 0;
#endif /* defined(INET) */
#if defined(INET6)
has_tso6 = tp->t_in6pcb != NULL &&
#if defined(IPSEC) || defined(FAST_IPSEC)
IPSEC_PCB_SKIP_IPSEC(tp->t_in6pcb->in6p_sp,
IPSEC_DIR_OUTBOUND) &&
#endif
tp->t_in6pcb->in6p_route.ro_rt != NULL &&
(tp->t_in6pcb->in6p_route.ro_rt->rt_ifp->if_capenable &
IFCAP_TSOv6) != 0;
#endif /* defined(INET6) */
has_tso = (has_tso4 || has_tso6) && !alwaysfrag;
/*
* Restart Window computation. From draft-floyd-incr-init-win-03:
@ -889,6 +910,9 @@ again:
* sure that we send equal size transfers down the
* stack (rather than big-small-big-small-...).
*/
#if IPV6_MAXPACKET != IP_MAXPACKET
#error IPV6_MAXPACKET != IP_MAXPACKET
#endif
len = (min(len, IP_MAXPACKET) / txsegsize) * txsegsize;
if (len <= txsegsize) {
use_tso = 0;
@ -1405,12 +1429,17 @@ send:
#ifdef INET6
case AF_INET6:
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
m->m_pkthdr.csum_flags = M_CSUM_TCPv6;
if (len + optlen) {
/* Fixup the pseudo-header checksum. */
/* XXXJRT: Not IPv6 Jumbogram safe. */
th->th_sum = in_cksum_addword(th->th_sum,
htons((u_int16_t) (len + optlen)));
if (use_tso) {
m->m_pkthdr.segsz = txsegsize;
m->m_pkthdr.csum_flags = M_CSUM_TSOv6;
} else {
m->m_pkthdr.csum_flags = M_CSUM_TCPv6;
if (len + optlen) {
/* Fixup the pseudo-header checksum. */
/* XXXJRT: Not IPv6 Jumbogram safe. */
th->th_sum = in_cksum_addword(th->th_sum,
htons((u_int16_t) (len + optlen)));
}
}
break;
#endif
@ -1764,3 +1793,124 @@ quit:
return error;
}
#endif /* defined(INET) */
#if defined(INET6)
/*
* tcp6_segment: handle M_CSUM_TSOv6 by software.
*
* => always consume m.
* => call output_func with output_arg for each segments.
*/
int
tcp6_segment(struct mbuf *m, int (*output_func)(void *, struct mbuf *),
void *output_arg)
{
int mss;
int iphlen;
int thlen;
int hlen;
int len;
struct ip6_hdr *iph;
struct tcphdr *th;
uint32_t tcpseq;
struct mbuf *hdr = NULL;
struct mbuf *t;
int error = 0;
KASSERT((m->m_flags & M_PKTHDR) != 0);
KASSERT((m->m_pkthdr.csum_flags & M_CSUM_TSOv6) != 0);
m->m_pkthdr.csum_flags = 0;
len = m->m_pkthdr.len;
KASSERT(len >= sizeof(*iph) + sizeof(*th));
if (m->m_len < sizeof(*iph)) {
m = m_pullup(m, sizeof(*iph));
if (m == NULL) {
error = ENOMEM;
goto quit;
}
}
iph = mtod(m, struct ip6_hdr *);
iphlen = sizeof(*iph);
KASSERT((iph->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
KASSERT(iph->ip6_nxt == IPPROTO_TCP);
hlen = iphlen + sizeof(*th);
if (m->m_len < hlen) {
m = m_pullup(m, hlen);
if (m == NULL) {
error = ENOMEM;
goto quit;
}
}
th = (void *)(mtod(m, char *) + iphlen);
tcpseq = ntohl(th->th_seq);
thlen = th->th_off * 4;
hlen = iphlen + thlen;
mss = m->m_pkthdr.segsz;
KASSERT(mss != 0);
KASSERT(len > hlen);
t = m_split(m, hlen, M_NOWAIT);
if (t == NULL) {
error = ENOMEM;
goto quit;
}
hdr = m;
m = t;
len -= hlen;
KASSERT(len % mss == 0);
while (len > 0) {
struct mbuf *n;
n = m_dup(hdr, 0, hlen, M_NOWAIT);
if (n == NULL) {
error = ENOMEM;
goto quit;
}
KASSERT(n->m_len == hlen); /* XXX */
t = m_split(m, mss, M_NOWAIT);
if (t == NULL) {
m_freem(n);
error = ENOMEM;
goto quit;
}
m_cat(n, m);
m = t;
KASSERT(n->m_len >= hlen); /* XXX */
n->m_pkthdr.len = hlen + mss;
iph = mtod(n, struct ip6_hdr *);
KASSERT((iph->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
iph->ip6_plen = htons(thlen + mss);
th = (void *)(mtod(n, char *) + iphlen);
th->th_seq = htonl(tcpseq);
th->th_sum = 0;
th->th_sum = in6_cksum(n, IPPROTO_TCP, iphlen, thlen + mss);
error = (*output_func)(output_arg, n);
if (error) {
goto quit;
}
tcpseq += mss;
len -= mss;
}
quit:
if (hdr != NULL) {
m_freem(hdr);
}
if (m != NULL) {
m_freem(m);
}
return error;
}
#endif /* defined(INET6) */

View File

@ -0,0 +1,38 @@
/* $NetBSD: in6_offload.h,v 1.1 2006/11/23 19:42:21 yamt Exp $ */
/*-
* Copyright (c)2005 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* subroutines to do software-only equivalent of h/w offloading.
*/
#if !defined(_NETINET6_IN6_OFFLOAD_H_)
#define _NETINET6_IN6_OFFLOAD_H_
int tcp6_segment(struct mbuf *, int (*)(void *, struct mbuf *), void *);
#endif /* !defined(_NETINET6_IN6_OFFLOAD_H_) */

View File

@ -1,4 +1,4 @@
/* $NetBSD: ip6_output.c,v 1.104 2006/11/16 01:33:45 christos Exp $ */
/* $NetBSD: ip6_output.c,v 1.105 2006/11/23 19:41:58 yamt Exp $ */
/* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
/*
@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.104 2006/11/16 01:33:45 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.105 2006/11/23 19:41:58 yamt Exp $");
#include "opt_inet.h"
#include "opt_inet6.h"
@ -91,6 +91,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.104 2006/11/16 01:33:45 christos Ex
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/in_offload.h>
#include <netinet6/in6_offload.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/nd6.h>
@ -142,6 +143,39 @@ static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
(((csum_flags) & M_CSUM_UDPv6) != 0 && udp_do_loopback_cksum) || \
(((csum_flags) & M_CSUM_TCPv6) != 0 && tcp_do_loopback_cksum)))
struct ip6_tso_output_args {
struct ifnet *ifp;
struct ifnet *origifp;
struct sockaddr_in6 *dst;
struct rtentry *rt;
};
static int ip6_tso_output_callback(void *, struct mbuf *);
static int ip6_tso_output(struct ifnet *, struct ifnet *, struct mbuf *,
struct sockaddr_in6 *, struct rtentry *);
static int
ip6_tso_output_callback(void *vp, struct mbuf *m)
{
struct ip6_tso_output_args *args = vp;
return nd6_output(args->ifp, args->origifp, m, args->dst, args->rt);
}
static int
ip6_tso_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
struct sockaddr_in6 *dst, struct rtentry *rt)
{
struct ip6_tso_output_args args;
args.ifp = ifp;
args.origifp = origifp;
args.dst = dst;
args.rt = rt;
return tcp6_segment(m, ip6_tso_output_callback, &args);
}
/*
* IP6 output. The packet in mbuf chain m contains a skeletal IP6
* header (with pri, len, nxt, hlim, src, dst).
@ -168,6 +202,7 @@ ip6_output(
struct ifnet *ifp, *origifp;
struct mbuf *m = m0;
int hlen, tlen, len, off;
boolean_t tso;
struct route_in6 ip6route;
struct rtentry *rt = NULL;
struct sockaddr_in6 *dst, src_sa, dst_sa;
@ -882,7 +917,7 @@ skip_ipsec2:;
* error, as we cannot handle this conflicting request
*/
tlen = m->m_pkthdr.len;
tso = (m->m_pkthdr.csum_flags & M_CSUM_TSOv6) != 0;
if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
dontfrag = 1;
else
@ -893,7 +928,7 @@ skip_ipsec2:;
error = EMSGSIZE;
goto bad;
}
if (dontfrag && tlen > IN6_LINKMTU(ifp)) { /* case 2-b */
if (dontfrag && (!tso && tlen > IN6_LINKMTU(ifp))) { /* case 2-b */
/*
* Even if the DONTFRAG option is specified, we cannot send the
* packet when the data length is larger than the MTU of the
@ -918,7 +953,8 @@ skip_ipsec2:;
/*
* transmit packet without fragmentation
*/
if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
if (dontfrag || (!alwaysfrag && (tlen <= mtu || tso))) {
/* case 1-a and 2-a */
struct in6_ifaddr *ia6;
int sw_csum;
@ -942,10 +978,20 @@ skip_ipsec2:;
m->m_pkthdr.csum_flags &= ~(M_CSUM_UDPv6|M_CSUM_TCPv6);
}
error = nd6_output(ifp, origifp, m, dst, rt);
if (__predict_true(!tso ||
(ifp->if_capenable & IFCAP_TSOv6) != 0)) {
error = nd6_output(ifp, origifp, m, dst, rt);
} else {
error = ip6_tso_output(ifp, origifp, m, dst, rt);
}
goto done;
}
if (tso) {
error = EINVAL; /* XXX */
goto bad;
}
/*
* try to fragment the packet. case 1-b and 3
*/

View File

@ -1,4 +1,4 @@
/* $NetBSD: mbuf.h,v 1.132 2006/11/17 12:33:39 ws Exp $ */
/* $NetBSD: mbuf.h,v 1.133 2006/11/23 19:41:58 yamt Exp $ */
/*-
* Copyright (c) 1996, 1997, 1999, 2001 The NetBSD Foundation, Inc.
@ -176,6 +176,7 @@ struct pkthdr {
#define M_CSUM_IPv4 0x00000040 /* IPv4 header */
#define M_CSUM_IPv4_BAD 0x00000080 /* IPv4 header checksum bad */
#define M_CSUM_TSOv4 0x00000100 /* TCPv4 segmentation offload */
#define M_CSUM_TSOv6 0x00000200 /* TCPv6 segmentation offload */
/* Checksum-assist quirks: keep separate from jump-table bits. */
#define M_CSUM_NO_PSEUDOHDR 0x80000000 /* Rx csum_data does not include
@ -185,7 +186,7 @@ struct pkthdr {
#define M_CSUM_BITS \
"\20\1TCPv4\2UDPv4\3TCP_UDP_BAD\4DATA\5TCPv6\6UDPv6\7IPv4\10IPv4_BAD" \
"\11TSOv4\40NO_PSEUDOHDR"
"\11TSOv4\12TSOv6\40NO_PSEUDOHDR"
/*
* Macros for manipulating csum_data on outgoing packets. These are