static int vr_flow_nat(unsigned short vrf, struct vr_flow_entry *fe, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { unsigned int ip_inc, inc = 0; unsigned short *t_sport, *t_dport; struct vrouter *router = pkt->vp_if->vif_router; struct vr_flow_entry *rfe; struct vr_ip *ip, *icmp_pl_ip; struct vr_icmp *icmph; bool hdr_update = false; if (fe->fe_rflow < 0) goto drop; rfe = vr_get_flow_entry(router, fe->fe_rflow); if (!rfe) goto drop; ip = (struct vr_ip *)pkt_data(pkt); if (ip->ip_proto == VR_IP_PROTO_ICMP) { icmph = (struct vr_icmp *)((unsigned char *)ip + (ip->ip_hl * 4)); if (vr_icmp_error(icmph)) { icmp_pl_ip = (struct vr_ip *)(icmph + 1); if (fe->fe_flags & VR_FLOW_FLAG_SNAT) { icmp_pl_ip->ip_daddr = rfe->fe_key.key_dest_ip; hdr_update = true; } if (fe->fe_flags & VR_FLOW_FLAG_DNAT) { icmp_pl_ip->ip_saddr = rfe->fe_key.key_src_ip; hdr_update = true; } if (hdr_update) icmp_pl_ip->ip_csum = vr_ip_csum(icmp_pl_ip); t_sport = (unsigned short *)((unsigned char *)icmp_pl_ip + (icmp_pl_ip->ip_hl * 4)); t_dport = t_sport + 1; if (fe->fe_flags & VR_FLOW_FLAG_SPAT) *t_dport = rfe->fe_key.key_dst_port; if (fe->fe_flags & VR_FLOW_FLAG_DPAT) *t_sport = rfe->fe_key.key_src_port; } } if ((fe->fe_flags & VR_FLOW_FLAG_SNAT) && (ip->ip_saddr == fe->fe_key.key_src_ip)) { vr_incremental_diff(ip->ip_saddr, rfe->fe_key.key_dest_ip, &inc); ip->ip_saddr = rfe->fe_key.key_dest_ip; } if (fe->fe_flags & VR_FLOW_FLAG_DNAT) { vr_incremental_diff(ip->ip_daddr, rfe->fe_key.key_src_ip, &inc); ip->ip_daddr = rfe->fe_key.key_src_ip; } ip_inc = inc; if (vr_ip_transport_header_valid(ip)) { t_sport = (unsigned short *)((unsigned char *)ip + (ip->ip_hl * 4)); t_dport = t_sport + 1; if (fe->fe_flags & VR_FLOW_FLAG_SPAT) { vr_incremental_diff(*t_sport, rfe->fe_key.key_dst_port, &inc); *t_sport = rfe->fe_key.key_dst_port; } if (fe->fe_flags & VR_FLOW_FLAG_DPAT) { vr_incremental_diff(*t_dport, rfe->fe_key.key_src_port, &inc); *t_dport = rfe->fe_key.key_src_port; } } #ifdef VROUTER_CONFIG_DIAG if (ip->ip_csum != VR_DIAG_IP_CSUM) vr_ip_update_csum(pkt, ip_inc, inc); #else vr_ip_update_csum(pkt, ip_inc, inc); #endif /* * If VRF is translated lets chose a new nexthop */ if ((fe->fe_flags & VR_FLOW_FLAG_VRFT) && pkt->vp_nh && pkt->vp_nh->nh_vrf != vrf) pkt->vp_nh = NULL; return vr_flow_forward(vrf, pkt, proto, fmd); drop: vr_pfree(pkt, VP_DROP_FLOW_NAT_NO_RFLOW); return 0; }
unsigned int vr_flow_inet_input(struct vrouter *router, unsigned short vrf, struct vr_packet *pkt, unsigned short proto, struct vr_forwarding_md *fmd) { struct vr_flow_key key, *key_p = &key; struct vr_ip *ip, *icmp_pl_ip = NULL; struct vr_fragment *frag; unsigned int flow_parse_res; unsigned int trap_res = 0; unsigned int sip, dip; unsigned short *t_hdr, sport, dport; unsigned char ip_proto; struct vr_icmp *icmph; /* * interface is in a mode where it wants all packets to be received * without doing lookups to figure out whether packets were destined * to me or not */ if (pkt->vp_flags & VP_FLAG_TO_ME) return vr_ip_rcv(router, pkt, fmd); ip = (struct vr_ip *)pkt_network_header(pkt); ip_proto = ip->ip_proto; /* if the packet is not a fragment, we easily know the sport, and dport */ if (vr_ip_transport_header_valid(ip)) { t_hdr = (unsigned short *)((char *)ip + (ip->ip_hl * 4)); if (ip_proto == VR_IP_PROTO_ICMP) { icmph = (struct vr_icmp *)t_hdr; if (vr_icmp_error(icmph)) { icmp_pl_ip = (struct vr_ip *)(icmph + 1); ip_proto = icmp_pl_ip->ip_proto; t_hdr = (unsigned short *)((char *)icmp_pl_ip + (icmp_pl_ip->ip_hl * 4)); if (ip_proto == VR_IP_PROTO_ICMP) icmph = (struct vr_icmp *)t_hdr; } } if (ip_proto == VR_IP_PROTO_ICMP) { if (icmph->icmp_type == VR_ICMP_TYPE_ECHO || icmph->icmp_type == VR_ICMP_TYPE_ECHO_REPLY) { sport = icmph->icmp_eid; dport = VR_ICMP_TYPE_ECHO_REPLY; } else { sport = 0; dport = icmph->icmp_type; } } else { if (icmp_pl_ip) { sport = *(t_hdr + 1); dport = *t_hdr; } else { sport = *t_hdr; dport = *(t_hdr + 1); } } } else { /* ...else, we need to get it from somewhere */ flow_parse_res = vr_flow_parse(router, NULL, pkt, &trap_res); /* ...and it really matters only if we need to do a flow lookup */ if (flow_parse_res == VR_FLOW_LOOKUP) { frag = vr_fragment_get(router, vrf, ip); if (!frag) { vr_pfree(pkt, VP_DROP_FRAGMENTS); return 0; } sport = frag->f_sport; dport = frag->f_dport; if (vr_ip_fragment_tail(ip)) vr_fragment_del(frag); } else { /* * since there is no other way of deriving a key, set the * key_p to NULL, indicating to code below that there is * indeed no need for flow lookup */ key_p = NULL; } } if (key_p) { /* we have everything to make a key */ if (icmp_pl_ip) { sip = icmp_pl_ip->ip_daddr; dip = icmp_pl_ip->ip_saddr; } else { sip = ip->ip_saddr; dip = ip->ip_daddr; } vr_get_flow_key(key_p, fmd->fmd_vlan, pkt, sip, dip, ip_proto, sport, dport); flow_parse_res = vr_flow_parse(router, key_p, pkt, &trap_res); if (flow_parse_res == VR_FLOW_LOOKUP && vr_ip_fragment_head(ip)) vr_fragment_add(router, vrf, ip, key_p->key_src_port, key_p->key_dst_port); if (flow_parse_res == VR_FLOW_BYPASS) { return vr_flow_forward(vrf, pkt, proto, fmd); } else if (flow_parse_res == VR_FLOW_TRAP) { return vr_trap(pkt, vrf, trap_res, NULL); } return vr_flow_lookup(router, vrf, key_p, pkt, proto, fmd); } /* * ...come here, when there is not enough information to do a * flow lookup */ return vr_flow_forward(vrf, pkt, proto, fmd); }
/** * vr_ip_transport_parse - parse IP packet to reach the L4 header */ int vr_ip_transport_parse(struct vr_ip *iph, struct vr_ip6 *ip6h, struct tcphdr **tcphp, unsigned int frag_size, void (do_tcp_mss_adj)(struct tcphdr *, unsigned short, unsigned char), unsigned int *hlenp, unsigned short *th_csump, unsigned int *tcph_pull_lenp, unsigned int *pull_lenp) { unsigned short ip_proto; bool thdr_valid = false; unsigned int hlen = 0, tcph_pull_len = 0; unsigned int pull_len = *pull_lenp; struct vr_tcp *tcph = NULL; unsigned short th_csum = 0; struct vr_icmp *icmph = NULL; struct vr_ip *icmp_pl_iph = NULL; struct vr_ip6 *icmp_pl_ip6h = NULL; /* Note: iph is set for both ipv4 and ipv6 cases */ if (iph) { if (vr_ip_is_ip6(iph)) { if (ip6h) { ip_proto = ip6h->ip6_nxt; hlen = sizeof(struct vr_ip6); thdr_valid = true; } else { return PKT_RET_UNHANDLED; } } else if (vr_ip_is_ip4(iph)) { ip_proto = iph->ip_proto; /* * Account for IP options */ thdr_valid = vr_ip_transport_header_valid(iph); if (thdr_valid) { hlen = iph->ip_hl * 4; pull_len += (hlen - sizeof(struct vr_ip)); } } else { return PKT_RET_UNHANDLED; } if (thdr_valid) { tcph_pull_len = pull_len; if (ip_proto == VR_IP_PROTO_TCP) { pull_len += sizeof(struct vr_tcp); } else if (ip_proto == VR_IP_PROTO_UDP) { pull_len += sizeof(struct vr_udp); } else if ((ip_proto == VR_IP_PROTO_ICMP) || (ip_proto == VR_IP_PROTO_ICMP6)) { pull_len += sizeof(struct vr_icmp); } else if (ip_proto == VR_IP_PROTO_SCTP) { pull_len += sizeof(struct vr_sctp); } if (frag_size < pull_len) { return PKT_RET_SLOW_PATH; } if (ip_proto == VR_IP_PROTO_TCP) { /* * Account for TCP options */ tcph = (struct vr_tcp *)((char *)iph + hlen); /* * If SYN, do TCP MSS adjust using passed callback, or send it * to the slow path. */ if ((ntohs(tcph->tcp_offset_r_flags) & VR_TCP_FLAG_SYN) && vr_to_vm_mss_adj) { if (do_tcp_mss_adj) { /* Kernel will never get here, it will return slow path */ do_tcp_mss_adj((struct tcphdr *)tcph, VROUTER_L2_OVERLAY_LEN, hlen); } else { return PKT_RET_SLOW_PATH; } } if ((VR_TCP_OFFSET(tcph->tcp_offset_r_flags) * 4) > (sizeof(struct vr_tcp))) { pull_len += ((VR_TCP_OFFSET(tcph->tcp_offset_r_flags) * 4) - (sizeof(struct vr_tcp))); if (frag_size < pull_len) { return PKT_RET_SLOW_PATH; } } th_csum = tcph->tcp_csum; } else if (ip_proto == VR_IP_PROTO_ICMP) { icmph = (struct vr_icmp *)((unsigned char *)iph + hlen); th_csum = icmph->icmp_csum; if (vr_icmp_error(icmph)) { pull_len += sizeof(struct vr_ip); if (frag_size < pull_len) return PKT_RET_SLOW_PATH; icmp_pl_iph = (struct vr_ip *)(icmph + 1); pull_len += (icmp_pl_iph->ip_hl * 4) - sizeof(struct vr_ip); if (frag_size < pull_len) return PKT_RET_SLOW_PATH; if (vr_ip_proto_pull(icmp_pl_iph)) { if (icmp_pl_iph->ip_proto == VR_IP_PROTO_TCP) pull_len += sizeof(struct vr_tcp); else if (icmp_pl_iph->ip_proto == VR_IP_PROTO_UDP) pull_len += sizeof(struct vr_udp); else pull_len += sizeof(struct vr_icmp); if (frag_size < pull_len) return PKT_RET_SLOW_PATH; if (icmp_pl_iph->ip_proto == VR_IP_PROTO_TCP) { th_csum = ((struct vr_tcp *) ((unsigned char *)icmp_pl_iph + icmp_pl_iph->ip_hl * 4))->tcp_csum; } else if (icmp_pl_iph->ip_proto == VR_IP_PROTO_UDP) { th_csum = ((struct vr_udp *) ((unsigned char *)icmp_pl_iph + icmp_pl_iph->ip_hl * 4))->udp_csum; } else if (icmp_pl_iph->ip_proto == VR_IP_PROTO_ICMP) { th_csum = ((struct vr_icmp *) ((unsigned char *)icmp_pl_iph + icmp_pl_iph->ip_hl * 4))->icmp_csum; } } } } else if (iph->ip_proto == VR_IP_PROTO_UDP) { th_csum = ((struct vr_udp *) ((unsigned char *)iph + hlen))->udp_csum; } else if ((ip_proto == VR_IP_PROTO_ICMP6) && ip6h) { icmph = (struct vr_icmp *)((unsigned char *)ip6h + hlen); if (icmph->icmp_type == VR_ICMP6_TYPE_NEIGH_SOL) { /* ICMP options size for neighbor solicit is 24 bytes */ pull_len += 24; } else if (icmph->icmp_type == VR_ICMP6_TYPE_ROUTER_SOL) { pull_len += 8; } else if (vr_icmp6_error(icmph)) { pull_len += sizeof(struct vr_ip6); if (frag_size < pull_len) return PKT_RET_SLOW_PATH; icmp_pl_ip6h = (struct vr_ip6 *)(icmph + 1); if (vr_ip6_proto_pull(icmp_pl_ip6h)) { if (icmp_pl_ip6h->ip6_nxt == VR_IP_PROTO_TCP) pull_len += sizeof(struct vr_tcp); else if (icmp_pl_ip6h->ip6_nxt == VR_IP_PROTO_UDP) pull_len += sizeof(struct vr_udp); else pull_len += sizeof(struct vr_icmp); if (frag_size < pull_len) return PKT_RET_SLOW_PATH; if (icmp_pl_ip6h->ip6_nxt == VR_IP_PROTO_TCP) { th_csum = ((struct vr_tcp *) ((unsigned char *)icmp_pl_ip6h + sizeof(struct vr_ip6)))->tcp_csum; } else if (icmp_pl_ip6h->ip6_nxt == VR_IP_PROTO_UDP) { th_csum = ((struct vr_udp *) ((unsigned char *)icmp_pl_ip6h + sizeof(struct vr_ip6)))->udp_csum; } else if (icmp_pl_ip6h->ip6_nxt == VR_IP_PROTO_ICMP6) { th_csum = ((struct vr_icmp *) ((unsigned char *)icmp_pl_ip6h + sizeof(struct vr_ip6)))->icmp_csum; } } } if (frag_size < pull_len) return PKT_RET_SLOW_PATH; } } } if (hlenp) *hlenp = hlen; if (th_csump) *th_csump = th_csum; if (tcph_pull_lenp) *tcph_pull_lenp = tcph_pull_len; if (tcphp) *tcphp = (struct tcphdr *)tcph; *pull_lenp = pull_len; return 0; }