/* *=========================================================================== * 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; }
/* *=========================================================================== * 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; }
/* *=========================================================================== * 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; }