On Wed, Jan 26, 2022 at 01:29:42AM +0100, Alexander Bluhm wrote: > Hi, > > There were some problems with ix(4) and ixl(4) hardware checksumming > for the output path on strict alignment architectures. > > I have merged jan@'s diffs and added some sanity checks and > workarounds. > > - If the first mbuf is not aligned or not contigous, use m_copydata() > to extract the IP, IPv6, TCP header. > - If the header is in the first mbuf, use m_data for the fast path. > - Add netstat counter for invalid header chains. This makes > us aware when hardware checksumming fails. > - Add netstat counter for header copies. This indicates that > better storage allocation in the network stack is possible. > It also allows to recognize alignment problems on non-strict > architectures. > - There is not risk of crashes on sparc64. > > Does this aproach make sense?
I think it is overly complicated and too much data is copied around. First of all there is no need to extract ipproto. The code can just use the M_TCP_CSUM_OUT and M_UDP_CSUM_OUT flags (they are not set for other protos). Because of this only they ip_hlen needs to be accessed and this can be done with m_getptr(). In the IP6 case even more can be skipped since ip_hlen is static for IPv6. In ixl(4) also the tcp header lenght needs to be extracted. Again the code can be simplified because HW checksumming is only enabled if ip_hlen == 5 and so the offset of the th_off field is static (for both IPv4 and IPv6). Again m_getptr can be used to just access the byte with th_off. Longterm in_proto_cksum_out() should probably help provide the th_off field. I think enforcing ip_hlen == 5 for UDP and TCP is fine, who needs IP options on UDP and TCP? > ix(4) works quite well, but finds some UDP packets that need copy. > ixl(4) has not been tested yet. I would like to have some feedback > for the idea first. > > bluhm > > Index: sys/dev/pci/if_ix.c > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/dev/pci/if_ix.c,v > retrieving revision 1.180 > diff -u -p -r1.180 if_ix.c > --- sys/dev/pci/if_ix.c 27 Jul 2021 01:44:55 -0000 1.180 > +++ sys/dev/pci/if_ix.c 25 Jan 2022 23:48:53 -0000 > @@ -1878,8 +1878,8 @@ ixgbe_setup_interface(struct ix_softc *s > #if NVLAN > 0 > ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; > #endif > - > ifp->if_capabilities |= IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; > + ifp->if_capabilities |= IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; > > /* > * Specify the media types supported by this sc and register > @@ -2437,12 +2437,6 @@ ixgbe_tx_ctx_setup(struct tx_ring *txr, > #else > struct ether_header *eh; > #endif > - struct ip *ip; > -#ifdef notyet > - struct ip6_hdr *ip6; > -#endif > - struct mbuf *m; > - int ipoff; > uint32_t vlan_macip_lens = 0, type_tucmd_mlhl = 0; > int ehdrlen, ip_hlen = 0; > uint16_t etype; > @@ -2511,29 +2505,46 @@ ixgbe_tx_ctx_setup(struct tx_ring *txr, > vlan_macip_lens |= ehdrlen << IXGBE_ADVTXD_MACLEN_SHIFT; > > switch (etype) { > - case ETHERTYPE_IP: > - if (mp->m_pkthdr.len < ehdrlen + sizeof(*ip)) > + case ETHERTYPE_IP: { > + struct ip *ip, ipdata; > + > + if (mp->m_pkthdr.len < ehdrlen + sizeof(*ip)) { > + ipstat_inc(ips_outbadcsum); > return (-1); > - m = m_getptr(mp, ehdrlen, &ipoff); > - KASSERT(m != NULL && m->m_len - ipoff >= sizeof(*ip)); > - ip = (struct ip *)(m->m_data + ipoff); > + } > + if (((mtod(mp, unsigned long) + ehdrlen) & ALIGNBYTES) == 0 && > + mp->m_len >= ehdrlen + sizeof(*ip)) { > + ip = (struct ip *)(mp->m_data + ehdrlen); > + } else { > + ipstat_inc(ips_outcpycsum); > + m_copydata(mp, ehdrlen, sizeof(ipdata), &ipdata); > + ip = &ipdata; > + } > ip_hlen = ip->ip_hl << 2; > ipproto = ip->ip_p; > type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4; > break; > -#ifdef notyet > - case ETHERTYPE_IPV6: > - if (mp->m_pkthdr.len < ehdrlen + sizeof(*ip6)) > + } > + case ETHERTYPE_IPV6: { > + struct ip6_hdr *ip6, ip6data; > + > + if (mp->m_pkthdr.len < ehdrlen + sizeof(*ip6)) { > + ip6stat_inc(ip6s_outbadcsum); > return (-1); > - m = m_getptr(mp, ehdrlen, &ipoff); > - KASSERT(m != NULL && m->m_len - ipoff >= sizeof(*ip6)); > - ip6 = (struct ip6 *)(m->m_data + ipoff); > + } > + if (((mtod(mp, unsigned long) + ehdrlen) & ALIGNBYTES) == 0 && > + mp->m_len >= ehdrlen + sizeof(*ip6)) { > + ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); > + } else { > + ip6stat_inc(ip6s_outcpycsum); > + m_copydata(mp, ehdrlen, sizeof(ip6data), &ip6data); > + ip6 = &ip6data; > + } > ip_hlen = sizeof(*ip6); > - /* XXX-BZ this will go badly in case of ext hdrs. */ > ipproto = ip6->ip6_nxt; > type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV6; > break; > -#endif > + } > default: > offload = FALSE; > break; > @@ -2552,6 +2563,10 @@ ixgbe_tx_ctx_setup(struct tx_ring *txr, > type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_UDP; > break; > default: > + if (mp->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) > + tcpstat_inc(tcps_outbadcsum); > + if (mp->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) > + udpstat_inc(udps_outbadcsum); > offload = FALSE; > break; > } > Index: sys/dev/pci/if_ixl.c > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/dev/pci/if_ixl.c,v > retrieving revision 1.78 > diff -u -p -r1.78 if_ixl.c > --- sys/dev/pci/if_ixl.c 9 Jan 2022 05:42:54 -0000 1.78 > +++ sys/dev/pci/if_ixl.c 25 Jan 2022 23:50:01 -0000 > @@ -71,6 +71,7 @@ > #include <net/if.h> > #include <net/if_dl.h> > #include <net/if_media.h> > +#include <net/route.h> > #include <net/toeplitz.h> > > #if NBPFILTER > 0 > @@ -82,6 +83,15 @@ > #endif > > #include <netinet/in.h> > +#include <netinet/ip.h> > +#include <netinet/ip_var.h> > +#include <netinet/ip6.h> > +#include <netinet6/ip6_var.h> > +#include <netinet/tcp.h> > +#include <netinet/tcp_timer.h> > +#include <netinet/tcp_var.h> > +#include <netinet/udp.h> > +#include <netinet/udp_var.h> > #include <netinet/if_ether.h> > > #include <dev/pci/pcireg.h> > @@ -1388,6 +1398,7 @@ static int ixl_rxeof(struct ixl_softc *, > static void ixl_rxfill(struct ixl_softc *, struct ixl_rx_ring *); > static void ixl_rxrefill(void *); > static int ixl_rxrinfo(struct ixl_softc *, struct if_rxrinfo *); > +static void ixl_rx_checksum(struct mbuf *, uint64_t); > > #if NKSTAT > 0 > static void ixl_kstat_attach(struct ixl_softc *); > @@ -1942,9 +1953,10 @@ ixl_attach(struct device *parent, struct > ifp->if_capabilities = IFCAP_VLAN_MTU; > #if 0 > ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; > - ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | > - IFCAP_CSUM_UDPv4; > #endif > + ifp->if_capabilities |= IFCAP_CSUM_IPv4; > + ifp->if_capabilities |= IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; > + ifp->if_capabilities |= IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; > > ifmedia_init(&sc->sc_media, 0, ixl_media_change, ixl_media_status); > > @@ -2771,6 +2783,119 @@ ixl_load_mbuf(bus_dma_tag_t dmat, bus_dm > BUS_DMA_STREAMING | BUS_DMA_NOWAIT)); > } > > +static int > +ixl_tx_setup_offload(struct mbuf *mp, uint64_t *cmd) > +{ > + uint64_t ip_hdr_len; > + uint8_t ipproto; > + > + switch (ntohs(mtod(mp, struct ether_header *)->ether_type)) { > + case ETHERTYPE_IP: { > + struct ip *ip, ipdata; > + > + if (mp->m_pkthdr.len < ETHER_HDR_LEN + sizeof(*ip)) { > + ipstat_inc(ips_outbadcsum); > + return (-1); > + } > + if (((mtod(mp, unsigned long) + ETHER_HDR_LEN) & ALIGNBYTES) > + == 0 && mp->m_len >= ETHER_HDR_LEN + sizeof(*ip)) { > + ip = (struct ip *)(mp->m_data + ETHER_HDR_LEN); > + } else { > + ipstat_inc(ips_outcpycsum); > + m_copydata(mp, ETHER_HDR_LEN, sizeof(ipdata), &ipdata); > + ip = &ipdata; > + } > + if (mp->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) > + *cmd |= IXL_TX_DESC_CMD_IIPT_IPV4_CSUM; > + else > + *cmd |= IXL_TX_DESC_CMD_IIPT_IPV4; > + > + ip_hdr_len = ip->ip_hl << 2; > + ipproto = ip->ip_p; > + break; > + } > +#ifdef INET6 > + case ETHERTYPE_IPV6: { > + struct ip6_hdr *ip6, ip6data; > + > + if (mp->m_pkthdr.len < ETHER_HDR_LEN + sizeof(*ip6)) { > + ip6stat_inc(ip6s_outbadcsum); > + return (-1); > + } > + if (((mtod(mp, unsigned long) + ETHER_HDR_LEN) & ALIGNBYTES) > + == 0 && mp->m_len >= ETHER_HDR_LEN + sizeof(*ip6)) { > + ip6 = (struct ip6_hdr *)(mp->m_data + ETHER_HDR_LEN); > + } else { > + ip6stat_inc(ip6s_outcpycsum); > + m_copydata(mp, ETHER_HDR_LEN, sizeof(ip6data), > + &ip6data); > + ip6 = &ip6data; > + } > + > + *cmd |= IXL_TX_DESC_CMD_IIPT_IPV6; > + > + ip_hdr_len = sizeof(*ip6); > + ipproto = ip6->ip6_nxt; > + break; > + } > +#endif > + default: > + return (-1); > + } > + > + *cmd |= (ETHER_HDR_LEN >> 1) << IXL_TX_DESC_MACLEN_SHIFT; > + *cmd |= (ip_hdr_len >> 2) << IXL_TX_DESC_IPLEN_SHIFT; > + > + switch (ipproto) { > + case IPPROTO_TCP: > + if (mp->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) { > + struct tcphdr *th, thdata; > + > + if (mp->m_pkthdr.len < ETHER_HDR_LEN + ip_hdr_len + > + sizeof(*th)) { > + tcpstat_inc(tcps_outbadcsum); > + return (-1); > + } > + if (((mtod(mp, unsigned long) + ETHER_HDR_LEN + > + ip_hdr_len) & ALIGNBYTES) == 0 && > + mp->m_len >= ETHER_HDR_LEN + ip_hdr_len + > + sizeof(*th)) { > + th = (struct tcphdr *)(mp->m_data + > + ETHER_HDR_LEN + ip_hdr_len); > + } else { > + tcpstat_inc(tcps_outcpycsum); > + m_copydata(mp, ETHER_HDR_LEN + ip_hdr_len, > + sizeof(thdata), &thdata); > + th = &thdata; > + } > + *cmd |= IXL_TX_DESC_CMD_L4T_EOFT_TCP; > + *cmd |= (uint64_t)th->th_off << > + IXL_TX_DESC_L4LEN_SHIFT; > + } > + break; > + case IPPROTO_UDP: > + if (mp->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) { > + if (mp->m_pkthdr.len < ETHER_HDR_LEN + ip_hdr_len + > + sizeof(struct udphdr)) { > + udpstat_inc(udps_outbadcsum); > + return (-1); > + } > + *cmd |= IXL_TX_DESC_CMD_L4T_EOFT_UDP; > + *cmd |= (sizeof(struct udphdr) >> 2) << > + IXL_TX_DESC_L4LEN_SHIFT; > + } > + break; > + default: > + if (mp->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) > + tcpstat_inc(tcps_outbadcsum); > + if (mp->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) > + udpstat_inc(udps_outbadcsum); > + return (-1); > + } > + > + return (0); > +} > + > static void > ixl_start(struct ifqueue *ifq) > { > @@ -2781,7 +2906,7 @@ ixl_start(struct ifqueue *ifq) > struct ixl_tx_map *txm; > bus_dmamap_t map; > struct mbuf *m; > - uint64_t cmd; > + uint64_t cmd, off = 0; > unsigned int prod, free, last, i; > unsigned int mask; > int post = 0; > @@ -2828,12 +2953,15 @@ ixl_start(struct ifqueue *ifq) > bus_dmamap_sync(sc->sc_dmat, map, 0, > map->dm_mapsize, BUS_DMASYNC_PREWRITE); > > + ixl_tx_setup_offload(m, &off); > + > for (i = 0; i < map->dm_nsegs; i++) { > txd = &ring[prod]; > > cmd = (uint64_t)map->dm_segs[i].ds_len << > IXL_TX_DESC_BSIZE_SHIFT; > cmd |= IXL_TX_DESC_DTYPE_DATA | IXL_TX_DESC_CMD_ICRC; > + cmd |= off; > > htolem64(&txd->addr, map->dm_segs[i].ds_addr); > htolem64(&txd->cmd, cmd); > @@ -3190,6 +3318,7 @@ ixl_rxeof(struct ixl_softc *sc, struct i > m->m_pkthdr.csum_flags |= M_FLOWID; > } > > + ixl_rx_checksum(m, word); > ml_enqueue(&ml, m); > } else { > ifp->if_ierrors++; /* XXX */ > @@ -3320,6 +3449,23 @@ ixl_rxrinfo(struct ixl_softc *sc, struct > free(ifr, M_TEMP, ixl_nqueues(sc) * sizeof(*ifr)); > > return (rv); > +} > + > +static void > +ixl_rx_checksum(struct mbuf *m, uint64_t word) > +{ > + if (!ISSET(word, IXL_RX_DESC_L3L4P)) > + return; > + > + if (ISSET(word, IXL_RX_DESC_IPE)) > + return; > + > + m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; > + > + if (ISSET(word, IXL_RX_DESC_L4E)) > + return; > + > + m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK; > } > > static int > Index: sys/dev/pci/ixgbe.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/dev/pci/ixgbe.h,v > retrieving revision 1.32 > diff -u -p -r1.32 ixgbe.h > --- sys/dev/pci/ixgbe.h 18 Jul 2020 07:18:22 -0000 1.32 > +++ sys/dev/pci/ixgbe.h 25 Jan 2022 22:25:56 -0000 > @@ -60,11 +60,20 @@ > > #include <net/if.h> > #include <net/if_media.h> > +#include <net/route.h> > #include <net/toeplitz.h> > > #include <netinet/in.h> > #include <netinet/if_ether.h> > #include <netinet/ip.h> > +#include <netinet/ip6.h> > +#include <netinet/ip_var.h> > +#include <netinet6/ip6_var.h> > +#include <netinet/tcp.h> > +#include <netinet/tcp_timer.h> > +#include <netinet/tcp_var.h> > +#include <netinet/udp.h> > +#include <netinet/udp_var.h> > > #if NBPFILTER > 0 > #include <net/bpf.h> > Index: sys/netinet/ip_var.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_var.h,v > retrieving revision 1.88 > diff -u -p -r1.88 ip_var.h > --- sys/netinet/ip_var.h 30 Mar 2021 08:37:11 -0000 1.88 > +++ sys/netinet/ip_var.h 25 Jan 2022 18:07:02 -0000 > @@ -88,6 +88,8 @@ struct ipstat { > u_long ips_outswcsum; /* software checksummed on output */ > u_long ips_notmember; /* multicasts for unregistered groups */ > u_long ips_wrongif; /* packet received on wrong interface */ > + u_long ips_outbadcsum; /* output hardware checksum failed */ > + u_long ips_outcpycsum; /* output checksum needs copy */ > }; > > struct ipoption { > @@ -133,6 +135,8 @@ enum ipstat_counters { > ips_outswcsum, /* software checksummed on output */ > ips_notmember, /* multicasts for unregistered groups */ > ips_wrongif, /* packet received on wrong interface */ > + ips_outbadcsum, /* output hardware checksum failed */ > + ips_outcpycsum, /* output checksum needs copy */ > > ips_ncounters > }; > Index: sys/netinet/tcp_var.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_var.h,v > retrieving revision 1.137 > diff -u -p -r1.137 tcp_var.h > --- sys/netinet/tcp_var.h 23 Jan 2022 21:44:31 -0000 1.137 > +++ sys/netinet/tcp_var.h 25 Jan 2022 22:19:42 -0000 > @@ -434,6 +434,9 @@ struct tcpstat { > u_int64_t tcps_sack_rcv_opts; /* SACK options received */ > u_int64_t tcps_sack_snd_opts; /* SACK options sent */ > u_int64_t tcps_sack_drop_opts; /* SACK options dropped */ > + > + u_int64_t tcps_outbadcsum; /* output hardware checksum failed */ > + u_int64_t tcps_outcpycsum; /* output checksum needs copy */ > }; > > /* > @@ -605,6 +608,9 @@ enum tcpstat_counters { > tcps_sack_rcv_opts, > tcps_sack_snd_opts, > tcps_sack_drop_opts, > + tcps_outbadcsum, > + tcps_outcpycsum, > + > tcps_ncounters, > }; > > Index: sys/netinet/udp_var.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/udp_var.h,v > retrieving revision 1.35 > diff -u -p -r1.35 udp_var.h > --- sys/netinet/udp_var.h 22 Aug 2020 17:54:57 -0000 1.35 > +++ sys/netinet/udp_var.h 25 Jan 2022 22:22:08 -0000 > @@ -68,6 +68,8 @@ struct udpstat { > /* output statistics: */ > u_long udps_opackets; /* total output packets */ > u_long udps_outswcsum; /* output software-csummed packets */ > + u_long udps_outbadcsum; /* output hardware checksum failed */ > + u_long udps_outcpycsum; /* output checksum needs copy */ > }; > > /* > @@ -111,6 +113,8 @@ enum udpstat_counters { > /* output statistics: */ > udps_opackets, /* total output packets */ > udps_outswcsum, /* output software-csummed packets */ > + udps_outbadcsum, /* output hardware checksum failed */ > + udps_outcpycsum, /* output checksum needs copy */ > > udps_ncounters > }; > Index: sys/netinet6/ip6_var.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_var.h,v > retrieving revision 1.89 > diff -u -p -r1.89 ip6_var.h > --- sys/netinet6/ip6_var.h 1 Dec 2021 12:51:09 -0000 1.89 > +++ sys/netinet6/ip6_var.h 25 Jan 2022 22:12:22 -0000 > @@ -199,6 +199,8 @@ struct ip6stat { > u_int64_t ip6s_forward_cachehit; > u_int64_t ip6s_forward_cachemiss; > u_int64_t ip6s_wrongif; > + u_int64_t ip6s_outbadcsum; > + u_int64_t ip6s_outcpycsum; > }; > > #ifdef _KERNEL > @@ -245,6 +247,9 @@ enum ip6stat_counters { > ip6s_forward_cachehit = ip6s_sources_deprecated + 16, > ip6s_forward_cachemiss, > ip6s_wrongif, > + ip6s_outbadcsum, > + ip6s_outcpycsum, > + > ip6s_ncounters, > }; > > Index: usr.bin/netstat/inet.c > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/usr.bin/netstat/inet.c,v > retrieving revision 1.173 > diff -u -p -r1.173 inet.c > --- usr.bin/netstat/inet.c 5 Dec 2021 22:36:19 -0000 1.173 > +++ usr.bin/netstat/inet.c 25 Jan 2022 23:11:34 -0000 > @@ -408,6 +408,8 @@ tcp_stats(char *name) > p(tcps_sndwinup, "\t\t%u window update packet%s\n"); > p(tcps_sndctrl, "\t\t%u control packet%s\n"); > p(tcps_outswcsum, "\t\t%u packet%s software-checksummed\n"); > + p(tcps_outbadcsum, "\t%llu packet%s output hardware checksum failed\n"); > + p(tcps_outcpycsum, "\t%llu packet%s output checksum needs copy\n"); > p(tcps_rcvtotal, "\t%u packet%s received\n"); > p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%u ack%s (for %llu > byte%s)\n"); > p(tcps_rcvdupack, "\t\t%u duplicate ack%s\n"); > @@ -540,6 +542,8 @@ udp_stats(char *name) > p1(udps_nosum, "\t%lu with no checksum\n"); > p(udps_inswcsum, "\t%lu input packet%s software-checksummed\n"); > p(udps_outswcsum, "\t%lu output packet%s software-checksummed\n"); > + p(udps_outbadcsum, "\t%lu packet%s output hardware checksum failed\n"); > + p(udps_outcpycsum, "\t%lu packet%s output checksum needs copy\n"); > p1(udps_noport, "\t%lu dropped due to no socket\n"); > p(udps_noportbcast, "\t%lu broadcast/multicast datagram%s dropped due > to no socket\n"); > p1(udps_nosec, "\t%lu dropped due to missing IPsec protection\n"); > @@ -610,6 +614,8 @@ ip_stats(char *name) > p(ips_badaddr, "\t%lu datagram%s with bad address in header\n"); > p(ips_inswcsum, "\t%lu input datagram%s software-checksummed\n"); > p(ips_outswcsum, "\t%lu output datagram%s software-checksummed\n"); > + p(ips_outbadcsum, "\t%lu packet%s output hardware checksum failed\n"); > + p(ips_outcpycsum, "\t%lu packet%s output checksum needs copy\n"); > p(ips_notmember, "\t%lu multicast packet%s which we don't join\n"); > p(ips_wrongif, "\t%lu packet%s received on wrong interface\n"); > #undef p > Index: usr.bin/netstat/inet6.c > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/usr.bin/netstat/inet6.c,v > retrieving revision 1.55 > diff -u -p -r1.55 inet6.c > --- usr.bin/netstat/inet6.c 26 Jan 2021 18:22:35 -0000 1.55 > +++ usr.bin/netstat/inet6.c 25 Jan 2022 23:11:34 -0000 > @@ -372,6 +372,8 @@ ip6_stats(char *name) > p(ip6s_badscope, "\t%llu packet%s that violated scope rules\n"); > p(ip6s_notmember, "\t%llu multicast packet%s which we don't join\n"); > p(ip6s_wrongif, "\t%llu packet%s received on wrong interface\n"); > + p(ip6s_outbadcsum, "\t%llu packet%s output hardware checksum failed\n"); > + p(ip6s_outcpycsum, "\t%llu packet%s output checksum needs copy\n"); > for (first = 1, i = 0; i < 256; i++) > if (ip6stat.ip6s_nxthist[i] != 0) { > if (first) { > -- :wq Claudio