From 010fc0d9a84dfadbf109938e774d4bc206076bfa Mon Sep 17 00:00:00 2001 From: "Anand H. Krishnan" Date: Fri, 23 Sep 2016 13:51:13 +0530 Subject: [PATCH] Identify IPv6 diagnostic packets Diagnostic packets are identified by two characteristics of the packet . wrong layer 4 checksum . layer 4 checksum of 0xFFFF When vRouter receives a packet and if it identifies that the packet has a checksum of 0xFFFF, it tries to verify the checksum. If the checksum turns out to be wrong, it marks the packet as a diagnostic packet. Before this change, we only used to verify checksum for IPv4 packets. Extend that logic to IPv6 packets too. Change-Id: Ida650cf02ad2bcee1fc1716b6903544068947a37 Partial-Bug: #1613999 --- linux/vrouter_mod.c | 164 +++++++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 62 deletions(-) diff --git a/linux/vrouter_mod.c b/linux/vrouter_mod.c index 578dc579e..9b939394e 100644 --- a/linux/vrouter_mod.c +++ b/linux/vrouter_mod.c @@ -943,9 +943,19 @@ lh_csum_verify_fast(struct vr_ip *iph, void *transport_hdr, unsigned char proto, unsigned int size) { __wsum csum; + struct ipv6hdr *ip6; + + if (vr_ip_is_ip4(iph)) { + csum = csum_tcpudp_nofold(iph->ip_saddr, iph->ip_daddr, + size, proto, 0); + } else if (vr_ip_is_ip6(iph)) { + ip6 = (struct ipv6hdr *)iph; + csum = ~csum_unfold(csum_ipv6_magic(&ip6->saddr, &ip6->daddr, + size, proto, 0)); + } else { + return -1; + } - csum = csum_tcpudp_nofold(iph->ip_saddr, iph->ip_daddr, - size, proto, 0); if (csum_fold(csum_partial(transport_hdr, size, csum))) { return -1; } @@ -960,9 +970,25 @@ lh_csum_verify_fast(struct vr_ip *iph, void *transport_hdr, unsigned static int lh_csum_verify(struct sk_buff *skb, struct vr_ip *iph) { - skb->csum = csum_tcpudp_nofold(iph->ip_saddr, iph->ip_daddr, - ntohs(iph->ip_len) - (iph->ip_hl * 4), - iph->ip_proto, 0); + uint8_t proto; + uint32_t size; + struct ipv6hdr *ip6; + + if (vr_ip_is_ip4(iph)) { + proto = iph->ip_proto; + size = ntohs(iph->ip_len) - (iph->ip_hl * 4); + skb->csum = csum_tcpudp_nofold(iph->ip_saddr, iph->ip_daddr, size, + proto, 0); + } else if (vr_ip_is_ip6(iph)) { + ip6 = (struct ipv6hdr *)iph; + proto = ip6->nexthdr; + size = ntohs(ip6->payload_len); + skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6->saddr, &ip6->daddr, + size, proto, 0)); + } else { + return -1; + } + if (__skb_checksum_complete(skb)) { return -1; } @@ -1057,20 +1083,20 @@ lh_pull_inner_headers_fast_udp(struct vr_packet *pkt, int int, unsigned short *), int *ret, int *encap_type) { - struct sk_buff *skb = vp_os_packet(pkt); - unsigned short pkt_headlen; + uint8_t proto; + int pkt_type = 0, helper_ret, parse_ret; + unsigned int frag_size, hdr_len, transport_size; + unsigned int pull_len, skb_pull_len, th_pull_len = 0, hlen = 0; + unsigned short th_csum = 0, pkt_headlen; + + void *th = NULL; unsigned char *va = NULL; - skb_frag_t *frag; - unsigned int frag_size, pull_len, hdr_len, skb_pull_len, tcp_size; - unsigned int th_pull_len = 0, hlen = 0; + struct sk_buff *skb = vp_os_packet(pkt); struct vr_ip *iph = NULL; struct vr_ip6 *ip6h = NULL; struct vr_udp *udph; - int pkt_type = 0; struct vr_ip *outer_iph = NULL; - unsigned short th_csum = 0; - void *th = NULL; - int helper_ret, parse_ret; + skb_frag_t *frag; pkt_headlen = pkt_head_len(pkt); hdr_len = sizeof(struct udphdr); @@ -1161,21 +1187,27 @@ lh_pull_inner_headers_fast_udp(struct vr_packet *pkt, int */ skb_push(skb, skb_pull_len); } - } else { + } else if (iph) { /* * We require checksum to be validated for TCP for GRO purpose * and in case of UDP for DIAG purpose. Rest all packets can * have the checksum validated in VM */ - if (!ip6h && iph && (!vr_ip_fragment(iph))) { - if (((iph->ip_proto == VR_IP_PROTO_UDP) && th_csum == VR_DIAG_CSUM) - || (iph->ip_proto == VR_IP_PROTO_TCP)) { + if (ip6h) { + proto = ip6h->ip6_nxt; + transport_size = ntohs(ip6h->ip6_plen); + } else { + proto = iph->ip_proto; + transport_size = ntohs(iph->ip_len) - hlen; + } + if ((!vr_ip_fragment(iph))) { + if (((proto == VR_IP_PROTO_UDP) && th_csum == VR_DIAG_CSUM) || + (proto == VR_IP_PROTO_TCP)) { lh_handle_checksum_complete_skb(skb); - if (skb_shinfo(skb)->nr_frags == 1) { - tcp_size = ntohs(iph->ip_len) - hlen; - if (lh_csum_verify_fast(iph, th, iph->ip_proto, tcp_size)) { + if (lh_csum_verify_fast(iph, th, + proto, transport_size)) { if (th_csum == VR_DIAG_CSUM) { vr_pkt_set_diag(pkt); } else { @@ -1183,11 +1215,9 @@ lh_pull_inner_headers_fast_udp(struct vr_packet *pkt, int } } } else { - /* - * Pull to the start of the transport header - */ + /* Pull to the start of the transport header */ skb_pull_len = (pkt_data(pkt) - skb->data) + - pkt_headlen + th_pull_len; + pkt_headlen + th_pull_len; skb_pull(skb, skb_pull_len); if (lh_csum_verify(skb, iph)) { @@ -1261,18 +1291,19 @@ lh_pull_inner_headers_fast_gre(struct vr_packet *pkt, int (*tunnel_type_cb)(unsigned int, unsigned int, unsigned short *), int *ret, int *encap_type) { - struct sk_buff *skb = vp_os_packet(pkt); - unsigned short pkt_headlen, *gre_hdr = NULL, gre_proto, - hdr_len = VR_GRE_BASIC_HDR_LEN; + uint8_t proto; + int pkt_type = 0, helper_ret, parse_ret; + unsigned short pkt_headlen, *gre_hdr = NULL, gre_proto; + unsigned short hdr_len = VR_GRE_BASIC_HDR_LEN, th_csum = 0; + unsigned int frag_size, hlen = 0, transport_size; + unsigned int pull_len, skb_pull_len, th_pull_len; + + void *th = NULL; unsigned char *va = NULL; - skb_frag_t *frag; - unsigned int frag_size, pull_len, hlen = 0, tcp_size, skb_pull_len, - th_pull_len = 0; - unsigned short th_csum = 0; + struct sk_buff *skb = vp_os_packet(pkt); struct vr_ip *iph = NULL; struct vr_ip6 *ip6h = NULL; - void *th = NULL; - int pkt_type = 0, helper_ret, parse_ret; + skb_frag_t *frag; pkt_headlen = pkt_head_len(pkt); if (pkt_headlen) { @@ -1410,16 +1441,21 @@ lh_pull_inner_headers_fast_gre(struct vr_packet *pkt, int * of the outer packet and this covers the inner packet too. */ - if (!skb_csum_unnecessary(skb)) { - if (!ip6h && iph && !vr_ip_fragment(iph)) { - if ((th_csum == VR_DIAG_CSUM && (iph->ip_proto == VR_IP_PROTO_UDP)) - || (iph->ip_proto == VR_IP_PROTO_TCP)) { + if (!skb_csum_unnecessary(skb) && iph) { + if (ip6h) { + proto = ip6h->ip6_nxt; + transport_size = ntohs(ip6h->ip6_plen); + } else { + proto = iph->ip_proto; + transport_size = ntohs(iph->ip_len) - hlen; + } + if (!vr_ip_fragment(iph)) { + if ((th_csum == VR_DIAG_CSUM && (proto == VR_IP_PROTO_UDP)) || + (proto == VR_IP_PROTO_TCP)) { lh_handle_checksum_complete_skb(skb); - if (skb_shinfo(skb)->nr_frags == 1) { - tcp_size = ntohs(iph->ip_len) - hlen; - if (lh_csum_verify_fast(iph, th, iph->ip_proto, tcp_size)) { + if (lh_csum_verify_fast(iph, th, proto, transport_size)) { if (th_csum == VR_DIAG_CSUM) { vr_pkt_set_diag(pkt); } else { @@ -1559,20 +1595,20 @@ lh_pull_inner_headers(struct vr_packet *pkt, unsigned short *)) { int pull_len, hlen, hoff, ret = 0; + uint32_t label, control_data; + unsigned short hdr_len, vrouter_overlay_len, eth_proto, l4_proto; + unsigned short udph_cksum = 0, th_csum = 0; + unsigned short icmp_pl_ip_proto; + unsigned int toff, skb_pull_len; + bool thdr_valid = false, mpls_pkt = true, outer_cksum_validate; + struct sk_buff *skb = vp_os_packet(pkt); - struct vr_ip *iph = NULL, *icmp_pl_iph = NULL; + struct vr_ip *iph = NULL, *icmp_pl_iph = NULL, *outer_iph = NULL; struct vr_ip6 *ip6h = NULL, *icmp_pl_ip6h = NULL; - unsigned short icmp_pl_ip_proto; struct tcphdr *tcph = NULL; struct vr_icmp *icmph = NULL; - unsigned int toff, skb_pull_len; - bool thdr_valid = false, mpls_pkt = true, outer_cksum_validate; - uint32_t label, control_data; struct vr_eth *eth = NULL; - unsigned short hdr_len, vrouter_overlay_len, eth_proto, l4_proto, udph_cksum = 0; - unsigned short th_csum = 0; struct udphdr *udph; - struct vr_ip *outer_iph = NULL; *reason = VP_DROP_PULL; pull_len = 0; @@ -1725,11 +1761,11 @@ lh_pull_inner_headers(struct vr_packet *pkt, l4_proto = ip6h->ip6_nxt; hlen = sizeof(struct vr_ip6); thdr_valid = true; - } else if (iph) { + } else { l4_proto = iph->ip_proto; thdr_valid = vr_ip_transport_header_valid(iph); + hlen = iph->ip_hl * 4; if (thdr_valid) { - hlen = iph->ip_hl * 4; pull_len += (hlen - sizeof(struct vr_ip)); } } @@ -1753,8 +1789,7 @@ lh_pull_inner_headers(struct vr_packet *pkt, */ iph = (struct vr_ip *) (skb->head + hoff); if (ip6h) - ip6h = (struct vr_ip6 *) iph; - + ip6h = (struct vr_ip6 *)iph; /* * Account for TCP options, if present @@ -1894,7 +1929,7 @@ lh_pull_inner_headers(struct vr_packet *pkt, } ip6h = (struct vr_ip6 *) (skb->head + hoff); - iph = (struct vr_ip*) ip6h; + iph = (struct vr_ip*)ip6h; } } lh_reset_skb_fields(pkt); @@ -1934,15 +1969,19 @@ lh_pull_inner_headers(struct vr_packet *pkt, */ skb_push(skb, skb_pull_len); if (tcph && vr_to_vm_mss_adj) { - lh_adjust_tcp_mss(tcph, skb, vrouter_overlay_len, sizeof(struct vr_ip)); + lh_adjust_tcp_mss(tcph, skb, vrouter_overlay_len, hlen); } } else { - if (!ip6h && !vr_ip_fragment(iph)) { - if (((th_csum == VR_DIAG_CSUM) && iph->ip_proto == VR_IP_PROTO_UDP) - || (iph->ip_proto == VR_IP_PROTO_TCP)) { - lh_handle_checksum_complete_skb(skb); - toff = (char *)((char *)iph + (iph->ip_hl * 4)) - (char *) skb->data; + if (ip6h) { + toff = (char *)((char *)ip6h + sizeof(*ip6h)) - (char *)skb->data; + } else { + toff = (char *)((char *)iph + (iph->ip_hl * 4)) - (char *)skb->data; + } + if (thdr_valid) { + if (((th_csum == VR_DIAG_CSUM) && l4_proto == VR_IP_PROTO_UDP) + || (l4_proto == VR_IP_PROTO_TCP)) { + lh_handle_checksum_complete_skb(skb); skb_pull(skb, toff); if (lh_csum_verify(skb, iph)) { if (th_csum == VR_DIAG_CSUM) { @@ -1956,8 +1995,9 @@ lh_pull_inner_headers(struct vr_packet *pkt, skb_push(skb, toff); } - if ((iph->ip_proto == VR_IP_PROTO_TCP) && vr_to_vm_mss_adj) { - lh_adjust_tcp_mss(tcph, skb, vrouter_overlay_len, sizeof(struct vr_ip)); + + if (tcph && vr_to_vm_mss_adj) { + lh_adjust_tcp_mss(tcph, skb, vrouter_overlay_len, hlen); } } }