while looking at outgoing ttl processing and rfc 6040, i noted that gif and gre patched a packets ttl, but didn't update the checksum on the packet.
i think there's two issues here. firstly, we need to update the checksum when the packet is patched, but only for v4, and secondly, we should clear the csum_flags on an mbuf after the first decap. this patch addresses the first issue. i have removed the ttl patching in gre and gif, but gif and the ipip code still patch the ecn on a packet. gif did not update the v4 checksum, and ipip recalculates the whole checksum. the stuff procter worked on in pf shows that you can do a small update to the checksum rather than recalcuate the whole thing. this adds a function to the ecn code that does just that for tos field, which is where the ecn is store. it also updates gif and ipip to use it. ok? Index: netinet/ip_ecn.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_ecn.c,v retrieving revision 1.8 diff -u -p -r1.8 ip_ecn.c --- netinet/ip_ecn.c 24 Sep 2016 14:51:37 -0000 1.8 +++ netinet/ip_ecn.c 14 Nov 2018 01:40:22 -0000 @@ -154,3 +154,19 @@ ip_ecn_egress(int mode, u_int8_t *outer, } return (1); } + +void +ip_tos_patch(struct ip *ip, uint8_t tos) +{ + uint16_t old; + uint16_t new; + uint32_t x; + + old = htons(ip->ip_tos); + new = htons(tos); + + ip->ip_tos = tos; + + x = ip->ip_sum + old - new; + ip->ip_sum = (x) + (x >> 16); +} Index: netinet/ip_ecn.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_ecn.h,v retrieving revision 1.6 diff -u -p -r1.6 ip_ecn.h --- netinet/ip_ecn.h 15 Mar 2012 16:37:11 -0000 1.6 +++ netinet/ip_ecn.h 14 Nov 2018 01:40:22 -0000 @@ -47,5 +47,6 @@ #if defined(_KERNEL) extern void ip_ecn_ingress(int, u_int8_t *, u_int8_t *); extern int ip_ecn_egress(int, u_int8_t *, u_int8_t *); +void ip_tos_patch(struct ip *, uint8_t); #endif /* _KERNEL */ #endif /* _NETINET_IP_ECN_H_ */ Index: netinet/ip_ipip.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_ipip.c,v retrieving revision 1.88 diff -u -p -r1.88 ip_ipip.c --- netinet/ip_ipip.c 28 Aug 2018 15:15:02 -0000 1.88 +++ netinet/ip_ipip.c 14 Nov 2018 01:40:22 -0000 @@ -239,16 +239,14 @@ ipip_input_if(struct mbuf **mp, int *off itos = ip->ip_tos; mode = m->m_flags & (M_AUTH|M_CONF) ? ECN_ALLOWED_IPSEC : ECN_ALLOWED; - if (!ip_ecn_egress(mode, &otos, &ip->ip_tos)) { + if (!ip_ecn_egress(mode, &otos, &tos)) { DPRINTF(("%s: ip_ecn_egress() failed\n", __func__)); ipipstat_inc(ipips_pdrops); goto bad; } /* re-calculate the checksum if ip_tos was changed */ - if (itos != ip->ip_tos) { - ip->ip_sum = 0; - ip->ip_sum = in_cksum(m, hlen); - } + if (itos != ip->ip_tos) + ip_tos_patch(ip, itos); break; #ifdef INET6 case IPPROTO_IPV6: Index: net/if_gif.c =================================================================== RCS file: /cvs/src/sys/net/if_gif.c,v retrieving revision 1.121 diff -u -p -r1.121 if_gif.c --- net/if_gif.c 14 Nov 2018 01:30:38 -0000 1.121 +++ net/if_gif.c 14 Nov 2018 01:40:22 -0000 @@ -805,7 +805,7 @@ gif_input(struct gif_tunnel *key, struct if (ip_ecn_egress(ECN_ALLOWED, &otos, &itos) == 0) goto drop; - ip->ip_tos = itos; + ip_tos_patch(ip, itos); m->m_pkthdr.ph_family = AF_INET; input = ipv4_input;