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;

Reply via email to