예제 #1
0
/*
 *===========================================================================
 *                    ipnet_gre_output_append
 *===========================================================================
 * Description: Handler for packets to send.
 * Parameters:  pkt - Packet to send and append GRE header to.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_gre_output_append(Ipnet_netif *netif, struct Ip_ip_tunnel_param *tunnel_param, Ipcom_pkt *pkt)
{
    Ipnet_pkt_gre          *gre_hdr;
    Ipnet_pkt_gre_opt_csum *csum_opt = IP_NULL;
#ifdef IPNET_USE_RFC2890
    Ip_u32                 *key_opt = IP_NULL;
    Ip_u32                 *key_opt_info;
    Ip_u32                 *seqnum_opt = IP_NULL;
    Ipnet_gre_t            *gre = netif->ipcom.pdrv;
#endif

    ip_assert(pkt->start >= (IPNET_GRE_MAX_HDR_SIZE + IPNET_IP_HDR_SIZE));

#ifdef IPNET_USE_RFC2890
    if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LINK1))
    {
        pkt->start -= IPNET_GRE_OPT_SEQNUM_SIZE;
        seqnum_opt = (Ip_u32 *) &pkt->data[pkt->start];
        IP_SET_HTONL(seqnum_opt, gre->send_seqnum);
        ++gre->send_seqnum;
    }

    if ((key_opt_info = ipcom_pkt_get_info(pkt, IPNET_PKT_INFO_GRE_KEY_OUTPUT)) != IP_NULL)
    {
        pkt->start -= IPNET_GRE_OPT_KEY_SIZE;
        key_opt = (Ip_u32 *) &pkt->data[pkt->start];
        IP_SET_HTONL(key_opt, *key_opt_info);
    }
#endif /* IPNET_USE_RFC2890 */

    if (IP_BIT_ISTRUE(tunnel_param->o_flags, IPCOM_TUNNEL_FLAG_GRE_CHECKSUM)
        || IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LINK2))
    {
        /* GRE output checksum enabled. */
        pkt->start -= IPNET_GRE_OPT_CSUM_SIZE;
        csum_opt = (Ipnet_pkt_gre_opt_csum *) &pkt->data[pkt->start];
        csum_opt->checksum  = 0;
        csum_opt->reserved1 = 0;
    }

    /* Add GRE header. */
    pkt->start -= IPNET_GRE_HDR_SIZE;
    gre_hdr = (Ipnet_pkt_gre *)&pkt->data[pkt->start];
    gre_hdr->flags_reserved0_ver = 0;
    switch (pkt->data[pkt->ipstart] & 0xf0)
    {
#ifdef IPCOM_USE_INET
    case 0x40:
        gre_hdr->protocol_type = IPNET_GRE_PROTOCOL_TYPE_IPV4;
        break;
#endif
#ifdef IPCOM_USE_INET6
    case 0x60:
        gre_hdr->protocol_type = IPNET_GRE_PROTOCOL_TYPE_IPV6;
        break;
#endif
    default:
        return -IP_ERRNO_EPROTONOSUPPORT;
    }

#ifdef IPNET_USE_RFC2890
    if (key_opt != IP_NULL)
        IP_BIT_SET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_KEY);

    if (seqnum_opt != IP_NULL)
        IP_BIT_SET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_SEQNUM);
#endif /* IPNET_USE_RFC2890 */

    if (csum_opt != IP_NULL)
    {
        /* The whole GRE header is written, time to calculate the checksum */
        IP_BIT_SET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_CHECKSUM);
        csum_opt->checksum = ipcom_in_checksum(gre_hdr, pkt->end - pkt->start);
    }

    return 0;
}
예제 #2
0
/*
 *===========================================================================
 *                    ipcom_forwarder_get_fw_if
 *===========================================================================
 * Context:     fw
 * Description: Obtain forwarding interface index corresponding to 'msg' from fw cache
 *              In case of a match the packet referred to by 'msg' is updated
 *              with destination hw address and IP header info.
 *              source MAC must be added before packet is sent.
 * Parameters:  'msg' : packet descriptor
 * Returns:     index of interface, or -1 if no fw cache entry matches 'msg'
 *
 */
IP_STATIC __inline__ int ipcom_forwarder_get_fw_if(fw_msg_t *msg)
{
#ifdef IPCOM_USE_INET
    fw_cache_entry_v4_t *e4;
    Ip_u32 dst4;
#endif
#ifdef IPCOM_USE_INET6
    fw_cache_entry_v6_t *e6;
#endif
#ifdef IPCOM_USE_FORWARDER_VLAN
    Ip_u16 vid;
    fw_cache_entry_vlan_t *ev;
#endif

#ifdef IPCOM_USE_FORWARDER_IPSEC
    fw_ipsec_replay_t fw_ipsec_replay;

    if (msg->ipsec_dec)
    {
        int offset = LL_ETH_HDR_SZ;
#ifdef IPCOM_USE_FORWARDER_VLAN
        if (IP_UNLIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                        msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN))
            offset = LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD;
#endif /* IPCOM_USE_FORWARDER_VLAN */

        if (ipcom_forwarder_ipsec_decrypt_packet(msg, &fw_ipsec_replay, offset) < 0)
        {
            msg->tbl[0] = IPCOM_FORWARDER_PACKET_IPSEC_DROP_PKT; /* Drop packet */
            return -1;
        }
    }
#endif /* IPCOM_USE_FORWARDER_IPSEC */

    if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4))
    {
#ifdef IPCOM_USE_INET
        /* IPv4 */
        e4 = &ipcom_fw.fw_cache_tbl_v4[msg->fw_key];
        dst4 = *(Ip_u32 *)(msg->packet + LL_ETH_HDR_SZ + 16);
        if (IP_UNLIKELY(dst4 != e4->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))

#endif
#endif
#endif
#endif
#endif
#endif
#endif
                goto nomatch;
        }

        IP_INCREMENTAL_CHECKSUM(((Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ)));
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &e4->mac_if, &e4->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ) <= 0)
        {
            Ipnet_pkt_ip *ip = (Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ);

            /* Restore IPv4 header */
            ip->ttl++;
            ip->sum = 0;
            ip->sum = ipcom_in_checksum(ip, IPCOM_IP_HEADER_LENGTH);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u32 *)(msg->packet - 2) = e4->mac_if.dst_mac[0];
        *(Ip_u32 *)(msg->packet + 2) = e4->mac_if.dst_mac[1];
        *(Ip_u32 *)(msg->packet + 6) = ipcom_fw.fw_port[e4->mac_if.ix[0]].src_type[0];
        *(Ip_u16 *)(msg->packet + 10) = ipcom_fw.fw_port[e4->mac_if.ix[0]].src_type[1];
#ifdef IPCOM_USE_FORWARDER_IPSEC
        *(Ip_u16 *)(msg->packet + 12) = ip_htons(IP_IPv4);
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        return e4->mac_if.ix[0];
#endif /* IPCOM_USE_INET */
    }
    else if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6))
    {
#ifdef IPCOM_USE_INET6
        /* IPv6 */
        e6 = &ipcom_fw.fw_cache_tbl_v6[msg->fw_key];

        if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != e6->ip6.addr[0])) ||
            IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
            IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
            || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
            )
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                )
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                    IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                    IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                    || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                    )
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                        IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                        IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                        || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                        )
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                            IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                            IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                            || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                            )
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                                IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                                IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                                || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                                )
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                                    IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                                    IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                                    || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                                    )
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                                        IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                                        IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                                        || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                                        )
#endif
#endif
#endif
#endif
#endif
#endif
#endif
                                        goto nomatch;
        }

        SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ), GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ)) - 1);
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &e6->mac_if, &e6->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ) <= 0)
        {
            /* Restore IPv6 header */
            SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ), GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ)) + 1);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u64 *)(msg->packet - 2) = e6->mac_if.dst_mac64;
        *(Ip_u64 *)(msg->packet + 6) = *(Ip_u64 *)&ipcom_fw.fw_port[e6->mac_if.ix[0]].src_type[0];

        return e6->mac_if.ix[0];
#endif /* IPCOM_USE_INET6 */
    }
#ifdef IPCOM_USE_FORWARDER_VLAN
    else if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN))
    {
#ifdef IPCOM_USE_INET
        /* IPv6 table is also used for VLAN entries, both IPv4 and IPv6 */
        ev = (fw_cache_entry_vlan_t *)&ipcom_fw.fw_cache_tbl_v6[msg->fw_key];
        dst4 = *(Ip_u32 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 16);
        vid = *(Ip_u16 *)(msg->packet + LL_ETH_HDR_SZ); /* UP/CFI/VID word */

        if (IP_UNLIKELY(ev->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                        dst4 != ev->ipvlan.a.addr_v4 ||
                        vid != ev->ipvlan.vlan_id ||
                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                            dst4 != ev->ipvlan.a.addr_v4 ||
                            vid != ev->ipvlan.vlan_id ||
                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                dst4 != ev->ipvlan.a.addr_v4 ||
                                vid != ev->ipvlan.vlan_id ||
                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                    dst4 != ev->ipvlan.a.addr_v4 ||
                                    vid != ev->ipvlan.vlan_id ||
                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                        dst4 != ev->ipvlan.a.addr_v4 ||
                                        vid != ev->ipvlan.vlan_id ||
                                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                            dst4 != ev->ipvlan.a.addr_v4 ||
                                            vid != ev->ipvlan.vlan_id ||
                                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                                dst4 != ev->ipvlan.a.addr_v4 ||
                                                vid != ev->ipvlan.vlan_id ||
                                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                                    dst4 != ev->ipvlan.a.addr_v4 ||
                                                    vid != ev->ipvlan.vlan_id ||
                                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#endif
#endif
#endif
#endif
#endif
#endif
#endif
                                        goto nomatch;
        }

        IP_INCREMENTAL_CHECKSUM(((Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD)));
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &ev->mac_if, &ev->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD) <= 0)
        {
            Ipnet_pkt_ip *ip = (Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD);

            /* Restore IPv4 header */
            ip->ttl--;
            ip->sum = 0;
            ip->sum = ipcom_in_checksum(ip, IPCOM_IP_HEADER_LENGTH);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u32 *)(msg->packet - 2) = ev->mac_if.dst_mac[0];
        *(Ip_u32 *)(msg->packet + 2) = ev->mac_if.dst_mac[1];
        *(Ip_u32 *)(msg->packet + 6) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[0];
        *(Ip_u32 *)(msg->packet + 10) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[1];
        *(Ip_u16 *)(msg->packet + 12) = ip_htons(IP_VLAN);
        *(Ip_u16 *)(msg->packet + 14) = ev->ipvlan.vlan_id;
#ifdef IPCOM_USE_FORWARDER_IPSEC
        *(Ip_u16 *)(msg->packet + 16) = ip_htons(IP_IPv4);
#endif
        return ev->mac_if.ix[0];
#endif /* IPCOM_USE_INET */
    }
    else if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN))
    {
#ifdef IPCOM_USE_INET6
        /* IPv6 table is also used for VLAN entries, both IPv4 and IPv6 */
        ev = (fw_cache_entry_vlan_t *)&ipcom_fw.fw_cache_tbl_v6[msg->fw_key];
        vid = *(Ip_u16 *)(msg->packet + LL_ETH_HDR_SZ); /* UP/CFI/VID word */

        if (IP_UNLIKELY(ev->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                        vid != ev->ipvlan.vlan_id ||
                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                            vid != ev->ipvlan.vlan_id ||
                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                vid != ev->ipvlan.vlan_id ||
                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                    vid != ev->ipvlan.vlan_id ||
                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                        vid != ev->ipvlan.vlan_id ||
                                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                            vid != ev->ipvlan.vlan_id ||
                                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                                vid != ev->ipvlan.vlan_id ||
                                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                                    vid != ev->ipvlan.vlan_id ||
                                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#endif
#endif
#endif
#endif
#endif
#endif
#endif
                                        goto nomatch;
        }


        SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD),
                    GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD)) - 1);
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &ev->mac_if, &ev->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD) <= 0)
        {
            /* Restore IPv6 header */
            SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD), GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD)) + 1);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u32 *)(msg->packet - 2) = ev->mac_if.dst_mac[0];
        *(Ip_u32 *)(msg->packet + 2) = ev->mac_if.dst_mac[1];
        *(Ip_u32 *)(msg->packet + 6) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[0];
        *(Ip_u32 *)(msg->packet + 10) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[1];
        *(Ip_u16 *)(msg->packet + 12) = ip_htons(IP_VLAN);
        *(Ip_u16 *)(msg->packet + 14) = ev->ipvlan.vlan_id;
#ifdef IPCOM_USE_FORWARDER_IPSEC
        *(Ip_u16 *)(msg->packet + 16) = ip_htons(IP_IPv6);
#endif
        return ev->mac_if.ix[0];
#endif /* IPCOM_USE_INET6 */
    }
#endif /* IPCOM_USE_FORWARDER_VLAN */

nomatch:

#ifdef IPCOM_USE_FORWARDER_IPSEC
    if (msg->ipsec_dec == 0xff)
    {
        int ret, offset = LL_ETH_HDR_SZ;

        if (msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
            msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN)
        {
            offset += LL_ETH_VLAN_ADD;
        }

#ifdef FW_IPSEC_DEBUG
        ipcom_printf("FW_IPSEC :: re-encrypt packet! offset=%d msg->fw_key: %x msg->len=%d(%d) msg->packet=%p(%p)\n",
                     offset, msg->fw_key, msg->len, ipcom_forwarder_ipnet_msg_len(msg),
                     msg->packet, ipcom_forwarder_ipnet_msg_packet(msg));

#endif

        /* Restore buffer pointer and length */
        msg->len    = ipcom_forwarder_ipnet_msg_len(msg);
        msg->packet = ipcom_forwarder_ipnet_msg_packet(msg);

        /* Re-encrypt packet */
        ret = ipcom_forwarder_ipsec_recrypt(&msg->packet[offset], msg->len - offset);
        if (ret < 0)
        {
#ifdef FW_IPSEC_DEBUG
            ipcom_printf("FW_IPSEC :: re-encrypt packet failed!!!\n");

#endif
            ipsec_recrypt_fail++;
            msg->tbl[0] = IPCOM_FORWARDER_PACKET_IPSEC_DROP_PKT; /* Drop packet */
            return -1;
        }
        ipsec_recrypt_ok++;
    }
#endif /* IPCOM_USE_FORWARDER_IPSEC */

    return -1;
}
예제 #3
0
/*
 *===========================================================================
 *                    ipnet_gre_input
 *===========================================================================
 * Description: Handler for received GRE packets.
 * Parameters:  pkt - Received GRE packet.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_gre_input(Ipnet_netif *netif, Ipcom_pkt *pkt)
{
    Ipnet_pkt_gre *gre_hdr;
    int            gre_hdr_start = pkt->start;

    IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_IPV4 | IPCOM_PKT_FLAG_IPV6);
    gre_hdr = (Ipnet_pkt_gre *)&pkt->data[pkt->start];
    pkt->start += IPNET_GRE_HDR_SIZE;

    /* First 16-bits non-null, verify bits and version. */
    if (gre_hdr->flags_reserved0_ver)
    {
        /* RFC 2784: 2.3. Reserved0 (bits 1-12)
           A receiver MUST discard a packet where any of bits 1-5 are non-zero,
           unless that receiver implements RFC 1701. Bits 6-12 are reserved for
           future use. These bits MUST be sent as zero and MUST be ignored on
           receipt.

           RFC 2890: 2. Extensions to GRE Header
           Key present (bit 2)
           Sequence Number Present (bit 3)
        */
        if (IP_BIT_ISSET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_MUSTBE0))
            goto cleanup;

        /* RFC 2784: 2.3.1. Version Number (bits 13-15)
           The Version Number field MUST contain the value zero.
        */
        if (IP_BIT_ISSET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_VERSION))
            goto cleanup;

        /* RFC 2784: 2.5. Checksum (2 octets)
           The Checksum field contains the IP (one's complement) checksum sum of
           the all the 16 bit words in the GRE header and the payload packet.
           For purposes of computing the checksum, the value of the checksum
           field is zero. This field is present only if the Checksum Present bit
           is set to one.
        */
        if (IPNET_GRE_CHECKSUM_PRESENT(gre_hdr))
        {
            pkt->start += IPNET_GRE_OPT_CSUM_SIZE;

            /* IP checksum must be ok. */
            if (ipcom_in_checksum(&gre_hdr->flags_reserved0_ver, pkt->end - gre_hdr_start) != 0)
                goto cleanup;
        }

#ifdef IPNET_USE_RFC2890
        /* RFC 2890: 2.1. Key Field (4 octets)
           The Key field contains a four octet number which was inserted by the
           encapsulator. The actual method by which this Key is obtained is
           beyond the scope of the document. The Key field is intended to be
           used for identifying an individual traffic flow within a tunnel. For
           example, packets may need to be routed based on context information
           not present in the encapsulated data.  The Key field provides this
           context and defines a logical traffic flow between encapsulator and
           decapsulator.  Packets belonging to a traffic flow are encapsulated
           using the same Key value and the decapsulating tunnel endpoint
           identifies packets belonging to a traffic flow based on the Key Field
           value.
        */
        if (IPNET_GRE_KEY_PRESENT(gre_hdr))
        {
            Ip_u32 key;

            key = IP_GET_NTOHL(&pkt->data[pkt->start]);
            IPNET_GRE_PKT_SET_KEY(pkt, key);
            pkt->start += IPNET_GRE_OPT_KEY_SIZE;
        }

        /* RFC 2890: 2.2. Sequence Number (4 octets)
           The Sequence Number field is a four byte field and is inserted by the
           encapsulator when Sequence Number Present Bit is set. The Sequence
           Number MUST be used by the receiver to establish the order in which
           packets have been transmitted from the encapsulator to the receiver.
           The intended use of the Sequence Field is to provide unreliable but
           in-order delivery. If the Key present bit (bit 2) is set, the
           sequence number is specific to the traffic flow identified by the Key
           field. Note that packets without the sequence bit set can be
           interleaved with packets with the sequence bit set.
        */
        if (IPNET_GRE_SEQNUM_PRESENT(gre_hdr))
        {
            Ipnet_gre_t *gre = netif->ipcom.pdrv;
            Ip_u32       seqnum;

            seqnum = IP_GET_NTOHL(&pkt->data[pkt->start]);
            if (seqnum == gre->recv_seqnum)
                /* In order segment */
                ++gre->recv_seqnum;
            else if (IPCOM_IS_LT(seqnum, gre->recv_seqnum))
                /* Out of order */
                goto cleanup;
            else
            {
                Ip_u32 outoforder_timer;
                int max_perflow_buffer;

                max_perflow_buffer = ipcom_sysvar_get_as_int0("ipnet.gre.MAX_PERFLOW_BUFFER", 3);
                outoforder_timer = ipcom_sysvar_get_as_int0("ipnet.gre.OUTOFORDER_TIMER", 1000);

                if (max_perflow_buffer <= 0)
                    /* No buffering allowed */
                    goto cleanup;

                while (ipcom_pqueue_size(gre->reassembly_queue) >= max_perflow_buffer)
                {
                    ipnet_timeout_cancel(gre->reassembly_tmo);
                    /* Call the timeout handler to force the oldest packet(s)
                       to be inputted to the stack and the receive sequence
                       number to advanced */
                    ipnet_gre_seq_tmo(netif);
                }

                if (gre_hdr->protocol_type == ip_htons(0x0800))
                    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV4);
                else if (gre_hdr->protocol_type == ip_htons(0x86DD))
                    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV6);

                IPNET_PKT_SET_TIMEOUT_ABS(pkt, ipnet->msec_now + outoforder_timer);
                if (gre->reassembly_tmo == IP_NULL)
                    if (ipnet_timeout_schedule(outoforder_timer,
                                               (Ipnet_timeout_handler) ipnet_gre_seq_tmo,
                                               netif,
                                               &gre->reassembly_tmo) < 0)
                        goto cleanup;
                if (ipcom_pqueue_insert(gre->reassembly_queue, pkt) != IPCOM_SUCCESS)
                {
                    ipnet_timeout_cancel(gre->reassembly_tmo);
                    goto cleanup;
                }
                return 0;
            }
            pkt->start += IPNET_GRE_OPT_SEQNUM_SIZE;
        }
#endif /* IPNET_USE_RFC2890 */
    }

    /**/
    if (netif->ipcom.link_tap)
        /* start is PAYLOAD == the inner IP header; ipstart points to the PREVIOUS
         * IP header */
        netif->ipcom.link_tap(&netif->ipcom, pkt, IP_PACKET_HOST, gre_hdr->protocol_type, pkt->start, pkt->start);

#ifdef IPCOM_USE_INET
    /* IPv4 in GRE. */
    if (gre_hdr->protocol_type == ip_htons(0x0800))
        return ipnet_ip4_input(netif, pkt);
#endif

#ifdef IPCOM_USE_INET6
    /* IPv6 in GRE. */
    if (gre_hdr->protocol_type == ip_htons(0x86DD))
        return ipnet_ip6_input(netif, pkt);
#endif

    /* Unsupported Protocol Type, drop. */

 cleanup:
    ipcom_pkt_free(pkt);
    return -IP_ERRNO_EINVAL;
}