Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
/**
 * 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;
}