implement ipv6 TSO.
partly from Matthias Scheler. tested by him.
This commit is contained in:
parent
22b629efe2
commit
809ec70bcf
@ -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
|
||||
|
@ -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 },
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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) */
|
||||
|
38
sys/netinet6/in6_offload.h
Normal file
38
sys/netinet6/in6_offload.h
Normal 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_) */
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user