/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_mpls.c,v $ $Revision: 1.73.10.5 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_mpls.c,v $ * $Author: hsun $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Jonas Rendel ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPMPLS #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include "ipnet_h.h" #include "ipnet_pkt_queue.h" #include "ipnet_netif.h" #include "ipnet_loopback.h" #include "ipnet_eth.h" #include "ipnet_mpls.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* Number of bits in the host part (EUI-64) when using the * MPLS to create a IPv6 address (RFC 2464?) */ #define IPMPLS_IP6_ID_BITS 64 /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC int ipnet_mpls_link_ioctl(struct Ipnet_netif_struct *netif, Ip_u32 command, void *data); IP_STATIC int ipnet_mpls_ioevent(Ipcom_netif *netif, int command, void *data, int fcflags); IP_STATIC int ipnet_mpls_tunnel_output(Ipnet_neigh *neigh, Ipcom_pkt *pkt); IP_STATIC int ipnet_mpls_shortcut_route_output(Ipnet_neigh *neigh, Ipcom_pkt *pkt); /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_STATIC Ipcom_hash *ipnet_mpls_fibif = IP_NULL; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_mpls_for_each_rtab_cb *=========================================================================== * Description: Callback that is called for each route table in the system. * Parameters: rtab - One of the route tables in the system. * data - User supplied data * Returns: * */ IP_STATIC void ipnet_mpls_for_each_rtab_cb(Ipcom_route *rtab, void *data) { Ipnet_route_entry *rt; (void)data; rt = ipnet_route_first_entry(IPNET_ROUTE_GET_FAMILY(rtab), IPNET_ROUTE_GET_VR(rtab), IPNET_ROUTE_GET_TABLE(rtab)); while (rt != IP_NULL) { ipnet_mpls_shortcut_route_clear(rt); rt = ipnet_route_next_entry(rt); } } /* *=========================================================================== * ipnet_mpls_ioctl *=========================================================================== * Description: MPLS IO control. * Parameters: netif - The interface on which the multicast address will * be used. * command - ioctl command. * data - Depends on command. * Returns: 0 = success, < 0 = error code * */ IP_STATIC int ipnet_mpls_link_ioctl(Ipnet_netif *netif, Ip_u32 command, void *data) { #ifdef IPCOM_USE_INET struct Ip_ifreq *ifreq = data; struct Ip_sockaddr_in *in_addr; Ipnet_ppp_peer *p = netif->private_data; #endif switch (command) { case IPMPLS_SIOCMPLSTUNNELDEL: ipnet_if_clean_ppp_peer(netif->private_data); ipcom_free(netif->private_data); netif->private_data = IP_NULL; /* Use break here since this ioctl must be propagated to the MPLS module */ break; #ifdef IPCOM_USE_INET case IP_SIOCGIFDSTADDR: in_addr = (struct Ip_sockaddr_in *) &ifreq->ip_ifr_dstaddr; ipcom_memset(in_addr, 0, sizeof(struct Ip_sockaddr_in)); in_addr->sin_family = IP_AF_INET; IPCOM_SA_LEN_SET(in_addr, sizeof(struct Ip_sockaddr_in)); in_addr->sin_addr.s_addr = p->peer4.s_addr; return 0; case IP_SIOCSIFDSTADDR: in_addr = (struct Ip_sockaddr_in *) &ifreq->ip_ifr_dstaddr; if (in_addr->sin_family != IP_AF_INET || IPCOM_SA_LEN_GET(in_addr) < sizeof(struct Ip_sockaddr_in)) return -IP_ERRNO_EINVAL; return ipnet_if_set_ipv4_ppp_peer(p, &in_addr->sin_addr); #endif /* IPCOM_USE_INET */ default: break; } return ipmpls_tunnel_ioctl(&netif->ipcom, command, data); } /* *=========================================================================== * ipnet_mpls_tunnel_output *=========================================================================== * Description: Outputs a packet to a MPLS tunnel interface. * Parameters: * Returns: 0 = success, <0 = error code. * */ IP_STATIC int ipnet_mpls_tunnel_output(Ipnet_neigh *neigh, Ipcom_pkt *pkt) { struct Ipnet_netif_struct *netif; if (neigh == IP_NULL) return -IP_ERRNO_EINVAL; netif = neigh->netif; netif->obytes += pkt->end - pkt->start; netif->opackets++; return ipmpls_tunnel_output(&netif->ipcom, pkt); } /* *=========================================================================== * ipnet_mpls_shortcut_route_output *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_mpls_shortcut_route_output(Ipnet_neigh *neigh, Ipcom_pkt *pkt) { Ip_u32 key; if (neigh->netif->ipcom.type == IP_IFT_MPLS_FIBIF) { ipcom_memcpy(&key, neigh->lladdr, sizeof(Ip_u32)); return ipmpls_shortcut_route_output(key, pkt); } return -IP_ERRNO_EHOSTUNREACH; } /* *=========================================================================== * ipnet_mpls_ioevent *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_mpls_ioevent(Ipcom_netif *netif, int command, void *data, int fcflags) { (void)netif; (void)command; (void)data; (void)fcflags; return 0; } /* *=========================================================================== * ipnet_mpls_get_proto *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_u16 ipnet_mpls_get_proto(Ipcom_pkt *pkt) { switch(pkt->data[pkt->ipstart] & 0xf0) { #ifdef IPCOM_USE_INET case 0x40: return IPNET_ETHFRAME_IP; #endif #ifdef IPCOM_USE_INET6 case 0x60: return IPNET_ETHFRAME_IPV6; #endif default: return 0; } } /* *=========================================================================== * ipnet_mpls_ifs_obj_hash *=========================================================================== * Description: Creates a hash value from a network interface. * Parameters: netif - A MPLS tunnel interface * Returns: hash value * */ IP_STATIC unsigned ipnet_mpls_ifs_obj_hash(Ipnet_netif *netif) { return (unsigned) netif->vr_index; } /* *=========================================================================== * ipnet_mpls_ifs_key_hash *=========================================================================== * Description: Creates a hash value from a virtual router index. * Parameters: vr_index - A pointer to a virtual router index. * Returns: hash value * */ IP_STATIC unsigned ipnet_mpls_ifs_key_hash(Ip_u16 *vr_index) { return (unsigned) *vr_index; } /* *=========================================================================== * ipnet_mpls_ifs_cmp *=========================================================================== * Description: Checks if the MPLS tunnel interface is assigned to the * specified virtual router. * Parameters: netif - A MPLS tunnel interface * vr_index - A pointer to a virtual router index. * Returns: IP_TRUE if they matches. * */ IP_STATIC unsigned ipnet_mpls_ifs_cmp(Ipnet_netif *netif, Ip_u16 *vr_index) { return netif->vr_index == *vr_index; } /* *=========================================================================== * ipnet_mpls_prepare_eth_hdr *=========================================================================== * Description: Writes a temporary header with the correct frame type * while neighbor discovery is performed. * Parameters: netif - egress interface * pkt - a MPLS packet * Returns: * */ IP_STATIC void ipnet_mpls_prepare_eth_hdr(Ipnet_netif *netif, Ipcom_pkt *pkt) { Ip_u16 type; if (pkt->force_frame_type == 0) type = ipnet_mpls_get_proto(pkt); else type = pkt->force_frame_type; /* * The destination MAC isn't known at this time. The local MAC is * just used since ipnet_eth_add_hdr() requires a pointer to a * valid MAC.. */ ipnet_eth_add_hdr(pkt, netif, netif->ipcom.link_addr, netif->ipcom.link_addr, type); } /* *=========================================================================== * ipnet_mpls_neigh_probe *=========================================================================== * Description: * * Parameters: * * Returns: * */ IP_STATIC int ipnet_mpls_neigh_probe(Ipnet_netif *netif, Ipcom_pkt *pkt, int domain, union Ip_in_addr_union *addr, Ipnet_mpls_probecb probecb) { Ipnet_neigh *neigh; int ret; if (probecb == IP_NULL) return -IP_ERRNO_EINVAL; neigh = ipnet_neigh_get(domain, addr, netif, IPNET_NEIGH_CAN_CREATE); if (neigh == IP_NULL) { ret = -IP_ERRNO_EINVAL; goto errout; } switch (neigh->state) { case IPNET_ND_STALE: ipnet_neigh_set_state(neigh, IPNET_ND_DELAY); /* Fall through */ case IPNET_ND_REACHABLE: case IPNET_ND_DELAY: case IPNET_ND_PROBE: probecb(netif, pkt, neigh->lladdr); /*lint !e522 */ break; case IPNET_ND_INCOMPLETE: ipnet_mpls_prepare_eth_hdr(netif, pkt); neigh->tx_action(neigh, pkt); break; default: IP_PANIC(); ret = -IP_ERRNO_EINVAL; goto errout; } ipnet_neigh_release(neigh); return 0; errout: ipnet_neigh_release(neigh); ip_assert(ret < 0); ipcom_pkt_free(pkt); return ret; } /* *=========================================================================== * ipnet_mpls_arp_probe *=========================================================================== * Description: Send an forced ARP request. * Parameters: netif - The ethernet interface to send to ARP request from. * pkt - The packet to send if the ARP finish successfully. * sin - The IPv4 destination address for 'pkt' * probecb - A function that will be called when/if the ARP * request is successful. * cookie - Caller data that will be passed to the 'probecb' * Returns: 0 = success, <0 = error code. * */ #ifdef IPCOM_USE_INET IP_GLOBAL int ipnet_mpls_arp_probe(Ipnet_netif *netif, Ipcom_pkt *pkt, struct Ip_sockaddr_in *sin, Ipnet_mpls_probecb probecb) { union Ip_in_addr_union addr; if (sin == IP_NULL) return -IP_ERRNO_EINVAL; ipcom_memcpy(&addr.in, &sin->sin_addr, sizeof(struct Ip_in_addr)); return ipnet_mpls_neigh_probe(netif, pkt, IP_AF_INET, &addr, probecb); } #endif /* IPCOM_USE_INET */ /* *=========================================================================== * ipnet_mpls_ip6_neighbor_cache_probe *=========================================================================== * Description: Probes for a neighbor cache entry * Parameters: netif - interface to send packet on * pkt - packet to send * probecb - Callback to be called when probe is completed * Returns: * */ #ifdef IPCOM_USE_INET6 IP_GLOBAL int ipnet_mpls_ip6_neighbor_cache_probe(Ipnet_netif *netif, Ipcom_pkt *pkt, struct Ip_sockaddr_in6 *sin6, Ipnet_mpls_probecb probecb) { union Ip_in_addr_union addr; if (sin6 == IP_NULL) return -IP_ERRNO_EINVAL; ipcom_memcpy(&addr.in6, &sin6->sin6_addr, sizeof(struct Ip_in6_addr)); return ipnet_mpls_neigh_probe(netif, pkt, IP_AF_INET6, &addr, probecb); } #endif /* IPCOM_USE_INET6 */ /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_mpls_init *=========================================================================== * Description: Initiates the IPNET_MPLS module. * Parameters: - * Returns: 0 = success, <0 error code * */ IP_GLOBAL int ipnet_mpls_init(void) { ipnet_mpls_fibif = ipcom_hash_new((Ipcom_hash_obj_func) ipnet_mpls_ifs_obj_hash, (Ipcom_hash_key_func) ipnet_mpls_ifs_key_hash, (Ipcom_hash_cmp_func) ipnet_mpls_ifs_cmp); return ipnet_mpls_fibif != IP_NULL ? 0 : -1; } /* *=========================================================================== * ipnet_mpls_input *=========================================================================== * Description: Input handler for ethernet frames. pkt->start is the offset * to the ethernet header. * Parameters: pkt - The ethernet frame. * Returns: 0 = success, <0 error code * */ IP_GLOBAL int ipnet_mpls_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) { return ipmpls_input(&netif->ipcom, pkt, IP_FALSE); } /* *=========================================================================== * ipnet_mpls_route_table_notify *=========================================================================== * Description: Called when a route table is created or deleted in IPNET * Parameters: vr_index - Route table index. * is_added - Tells if table is added (or deleted) * Returns: - * */ IP_GLOBAL void ipnet_mpls_route_table_notify(Ip_u16 vr_index, Ip_bool is_added) { Ipnet_netif *fibif; fibif = ipcom_hash_get(ipnet_mpls_fibif, &vr_index); if (!is_added && fibif != IP_NULL) { /* Removed table. Delete corresponding MPLS FIB interface */ (void)ipcom_hash_remove(ipnet_mpls_fibif, fibif); ipmpls_if_free(&fibif->ipcom); ipcom_free(fibif->private_data); ipcom_free(fibif); } else if (is_added && fibif == IP_NULL) { /* New table. Create a new MPLS FIB interface to represent it */ fibif = ipcom_calloc(1, sizeof(Ipnet_netif)); if (fibif) { fibif->ipcom.metric = 1; fibif->ipcom.type = IP_IFT_MPLS_FIBIF; fibif->vr_index = (Ip_u16)vr_index; ipcom_snprintf(fibif->ipcom.name, IP_IFNAMSIZ, "mplsfib%d", vr_index); (void)ipnet_mpls_if_init(fibif); IP_BIT_SET(fibif->ipcom.flags, IP_IFF_UP | IP_IFF_RUNNING | IP_IFF_NOARP); if (ipcom_hash_add(ipnet_mpls_fibif, fibif) != IPCOM_SUCCESS) IP_PANIC(); ipnet_conf_update(fibif->ipcom.name, IP_NULL, IP_NULL); } } #if IPMPLS_MULTIPLE_ROUTE_TABLES ipmpls_route_table_notify(vr_index, is_added); #endif /* IPMPLS_MULTIPLE_ROUTE_TABLES */ } /* *=========================================================================== * ipnet_mpls_get_src_addr *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL IP_CONST void* ipnet_mpls_get_src_addr(Ip_sa_family_t sa_family, Ipnet_netif *fibif, void* dst) { Ip_u32 i; Ipnet_netif *this_netif; void* src = IP_NULL; #ifdef IPNET_ASSERT ip_assert(fibif != IP_NULL); #endif switch(sa_family) { #ifdef IPCOM_USE_INET case IP_AF_INET: { IP_CONST struct Ip_in_addr *first_valid = IP_NULL; IP_CONST struct Ip_in_addr *selected = IP_NULL; Ipnet_ip4_addr_entry *addr; Ip_u32 mask_n = 0; Ip_u32 dst_n; int longest_match_bits; dst_n = IP_GET_32ON16((struct Ip_in_addr*)dst); /* * IPNET_NETIF_FOR_EACH sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH(this_netif, i) { if (this_netif->ipcom.type == IP_IFT_LOOP || this_netif->vr_index != fibif->vr_index) continue; for (addr = this_netif->inet4_addr_list; addr != IP_NULL; addr = addr->next) { if (addr->type != IPNET_ADDR_TYPE_UNICAST) continue; if (!first_valid) first_valid = (struct Ip_in_addr *) &addr->ipaddr_n; mask_n = 0; longest_match_bits = IP_NETBITS(dst_n); ipnet_route_create_mask(&mask_n, longest_match_bits); while (longest_match_bits < 32 && ipcom_route_key_cmp(32, &addr->ipaddr_n, &dst_n, &mask_n)) { ipnet_route_create_mask(&mask_n, ++longest_match_bits); selected = (struct Ip_in_addr *) &addr->ipaddr_n; } } if (selected) { src = (void*)selected; goto finish; } } src = (void*)first_valid; } break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: { IP_CONST struct Ip_in6_addr *first_valid = IP_NULL; IP_CONST struct Ip_in6_addr *selected = IP_NULL; IP_CONST struct Ip_in6_addr *dst_addr; Ipnet_ip6_addr_entry *addr; int matched_16bits = -1; dst_addr = (IP_CONST struct Ip_in6_addr *)dst; /* * IPNET_NETIF_FOR_EACH sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH(this_netif, i) { if (this_netif->ipcom.type == IP_IFT_LOOP || this_netif->vr_index != fibif->vr_index) continue; for (addr = this_netif->inet6_addr_list; addr != IP_NULL; addr = addr->next) { if (IP_IN6_IS_ADDR_MULTICAST(&addr->addr) || IP_IN6_IS_ADDR_LINK_LOCAL(&addr->addr) || IP_IN6_IS_ADDR_SITE_LOCAL(&addr->addr) || IP_BIT_ISSET(addr->flags, IPNET_IP6_ADDR_FLAG_DEPRECATED)) continue; if (!first_valid) src = &addr->addr; if (IP_BIT_ISFALSE(addr->flags, IPNET_IP6_ADDR_FLAG_TENTATIVE)) { unsigned b; for (b = 0; b < (int) (sizeof(struct Ip_in6_addr) / sizeof(Ip_u16)); b++) if (dst_addr->in6.addr16[b] != addr->addr.in6.addr16[b]) break; if (matched_16bits < (int) b) { if ((b << 4) >= addr->prefixlen) { /* All of the prefix matched, use this address */ src = &addr->addr; goto finish; } /* Use the address with the longest match */ matched_16bits = b; selected = &addr->addr; } } } if (selected) { src = (void*)selected; goto finish; } } src = (void*)first_valid; } break; #endif /* IPCOM_USE_INET6 */ } finish: return src; } /* *=========================================================================== * ipnet_mpls_shortcut_route_setup *=========================================================================== * Description: Called when a new shortcut route is setup in IPNET. * Parameters: vr - Virtual router to use. * rt - Pointer to pseudo-wire route entry. * pnetif - Location where the outgoing interface should be stored. * Returns: 0 = success, <0 error code * */ IP_GLOBAL int ipnet_mpls_shortcut_route_setup(Ip_u16 vr, Ipnet_route_entry *rt, Ipnet_netif **pnetif) { rt->netif = ipcom_hash_get(ipnet_mpls_fibif, &vr); if (pnetif != IP_NULL) *pnetif = rt->netif; IP_BIT_SET(rt->hdr.flags, IPNET_RTF_LLINFO); return 0; } /* *=========================================================================== * ipnet_mpls_get_fibif *=========================================================================== * Description: get the fibif interface. * Parameters: vr - Virtual router to use. * Returns: 0 = success, <0 error code * */ IP_GLOBAL Ipnet_netif * ipnet_mpls_get_fibif(Ip_u16 vr) { return ipcom_hash_get(ipnet_mpls_fibif, &vr); } /* *=========================================================================== * ipnet_mpls_shortcut_route_setup *=========================================================================== * Description: Called when a new shortcut route is removed in IPNET. * Parameters: rt - Pointer to pseudo-wire route entry. * * Returns: - * */ IP_GLOBAL void ipnet_mpls_shortcut_route_clear(Ipnet_route_entry *rt) { (void)rt; } /* *=========================================================================== * ipnet_mpls_default_ip4_neigh_probe *=========================================================================== * Description: Sets the status of the neighbor to IPNET_ND_REACHABLE. * This behavior can be used by links that do not need to * resolve the peer link address (ex. loopback, PPP) or * by interfaces that do their own IP4-to-link-addr * mapping (IFF_NOARP flag set). * Parameters: neigh - a neighbor entry. * Returns: * */ #ifdef IPCOM_USE_INET IP_STATIC void ipnet_mpls_default_ip4_neigh_probe(Ipnet_neigh *neigh, Ip_bool unicast_probe) { IPCOM_UNUSED_ARG(unicast_probe); ipnet_neigh_set_state(neigh, IPNET_ND_REACHABLE); /* * The neigh->laddr is set to zero when the neighbor entry is * created. */ } #endif /* IPCOM_USE_INET */ /* *=========================================================================== * ipnet_mpls_if_init *=========================================================================== * Description: MPLS interface setup routine. * Parameters: netif - pointer to interface descriptor. * Returns: 0 = success, <0 error code * */ IP_GLOBAL int ipnet_mpls_if_init(Ipnet_netif *netif) { /* Setup link functions for MPLS use. */ netif->link_input = ipnet_mpls_input; netif->link_ioctl = ipnet_mpls_link_ioctl; netif->ipcom.link_ioevent = ipnet_mpls_ioevent; #ifdef IPCOM_USE_INET netif->inet4_neigh_probe = ipnet_mpls_default_ip4_neigh_probe; #endif switch (netif->ipcom.type) { case IP_IFT_MPLS_TUNNEL: netif->link_output = ipnet_mpls_tunnel_output; break; case IP_IFT_MPLS_FIBIF: netif->link_output = ipnet_mpls_shortcut_route_output; break; default: return -IP_ERRNO_EINVAL; } #ifdef IPCOM_USE_INET6 netif->inet6_if_id_bits = IPMPLS_IP6_ID_BITS; #endif netif->neigh_create = ipnet_netif_neigh_create; netif->private_data = ipcom_malloc(sizeof(Ipnet_ppp_peer)); if (netif->private_data == IP_NULL) return -IP_ERRNO_ENOMEM; ipnet_if_init_ppp_peer(netif, netif->private_data); return ipmpls_if_init(&netif->ipcom); } IP_GLOBAL void ipnet_mpls_probe_reply(Ipnet_netif *netif, Ipcom_pkt *pkt, Ip_u8 *dst) { /* Get frame type from PDU */ Ip_u16 type; if (pkt->force_frame_type == 0) type = ipnet_mpls_get_proto(pkt); else type = pkt->force_frame_type; ipnet_eth_add_hdr(pkt, netif, dst, netif->ipcom.link_addr, type); /* The interface is now known, what we should do is set the packet's * interface index so that it matches the output interface */ pkt->ifindex = netif->ipcom.ifindex; (void) ipnet_if_output(netif, pkt); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipcom_mpls_ioevent *=========================================================================== * Description: IPNET Protocol stack portation layer handler for the IPMPLS * module. * Parameters: netif - Pointer to IPCOM interface descriptor. * command - IPMPLS EIOX command. * data - Used for various purposes. * more - Used for various purposes. * Returns: 0 = success, <0 error code * */ IP_PUBLIC int ipcom_mpls_ioevent(Ipcom_netif *netif, int command, void *data, void *more, void *cookie) { Ipnet_netif *ipnet_netif = (Ipnet_netif *)netif; Ipnet_netif **pnetif = (Ipnet_netif **)more; int ret = 0; Ipcom_pkt *pkt = (Ipcom_pkt*)data; Ip_bool *penultimate = (Ip_bool*)cookie; switch(command) { case IPMPLS_EIOXBLOCKOK: if (ipnet_is_stack_task() == IP_FALSE) ret = 1; /* user process -> block is ok. */ break; case IPMPLS_EIOXDRVIOCTL: { Ipmpls_eio_ioctl *eio_ioctl = data; ip_assert(netif); ret = netif->drv_ioctl(netif, eio_ioctl->command, eio_ioctl->data); break; } case IPMPLS_EIOXGET_TTL: { Ip_u8 ttl; switch (ipnet_mpls_get_proto(pkt)) { #ifdef IPCOM_USE_INET case IPNET_ETHFRAME_IP: ttl = ((Ipnet_pkt_ip*)&pkt->data[pkt->ipstart])->ttl; break; #endif #ifdef IPCOM_USE_INET6 case IPNET_ETHFRAME_IPV6: ttl = ((Ipnet_pkt_ip6*)&pkt->data[pkt->ipstart])->hlim; break; #endif default: ttl = 255; break; } ret = (int)ttl; } break; case IPMPLS_EIOXGET_USER_VR: { Ip_u16 *vr = (Ip_u16*)data; if (vr == IP_NULL) return -IP_ERRNO_EINVAL; #if IPCOM_VR_MAX > 1 *vr = ipnet_get_effective_vr(); #else *vr = 0; #endif } break; case IPMPLS_EIOXOUTPUT_IF: ip_assert(ipnet_netif); ret = ipnet_if_output(ipnet_netif, pkt); break; case IPMPLS_EIOXRTABTONETIF: { Ip_u16 vr = *(Ip_u16*)data; *pnetif = ipnet_loopback_get_netif(vr); if (*pnetif == IP_NULL) ret = 1; break; } case IPMPLS_EIOXINDEXTONETIF: { Ip_u32 ifindex = *(Ip_u32*)data; *pnetif = ipnet_if_indextonetif(IPCOM_VR_ANY, ifindex); if (*pnetif == IP_NULL) ret = 1; break; } case IPMPLS_EIOXNAMETONETIF: { Ip_u16 vr = *(Ip_u16*)cookie; char *name = (char*)data; *pnetif = ipnet_if_nametonetif(vr, name); if (*pnetif == IP_NULL) ret = 1; break; } case IPMPLS_EIOXFOREACHNETIF: { Ipmpls_netif_foreach_cb cb = (Ipmpls_netif_foreach_cb)data; Ip_u32 i; if (cb) { #if IPMPLS_RELEASE >= 60101 /* * IPNET_NETIF_FOR_EACH sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH(ipnet_netif, i) cb(&ipnet_netif->ipcom, ipnet_netif->vr_index, more); #else /* * IPNET_NETIF_FOR_EACH sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH(ipnet_netif, i) cb(&ipnet_netif->ipcom, more); #endif } } break; case IPMPLS_EIOXIF_LOCK: ip_assert(netif); IPNET_IF_LOCK(ipnet_netif); break; case IPMPLS_EIOXIF_UNLOCK: ip_assert(netif); IPNET_IF_UNLOCK(ipnet_netif); break; case IPMPLS_EIOXIF_OPEN: ip_assert(netif); ipnet_kioevent(ipnet_netif, IP_EIOXUP, IP_NULL, IP_FLAG_FC_STACKCONTEXT); break; case IPMPLS_EIOXIF_RUNNING: ip_assert(netif); ipnet_kioevent(ipnet_netif, IP_EIOXRUNNING, IP_NULL, IP_FLAG_FC_STACKCONTEXT); break; case IPMPLS_EIOXIF_CLOSE: ip_assert(netif); ipnet_kioevent(ipnet_netif, IP_EIOXDOWN, IP_NULL, IP_FLAG_FC_STACKCONTEXT); break; case IPMPLS_EIOXIF_STOP: ip_assert(netif); ipnet_kioevent(ipnet_netif, IP_EIOXSTOP, IP_NULL, IP_FLAG_FC_STACKCONTEXT); break; #ifdef IPCOM_USE_INET case IPMPLS_EIOXINPUT_IN: ip_assert(ipnet_netif); ipnet_netif->ibytes += pkt->end - pkt->start; ipnet_netif->ipackets++; if (!more) ret = ipnet_ip4_input(ipnet_netif, pkt); else { /* Forward IPv4 packet via IPNET */ Ipnet_pkt_ip *ip_pkt; pkt->ipstart = pkt->start; ip_pkt = ipcom_pkt_get_iphdr(pkt); --ip_pkt->ttl; ip_pkt->sum = 0; ip_pkt->sum = ipcom_in_checksum(ip_pkt, (ip_pkt->v_hl & 0x0F) << 2); IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR | IPCOM_PKT_FLAG_FORWARDED); IPCOM_MIB2(ipForwDatagrams++); IPCOM_MIB2_SYSWI_U64_ADD(v4, ipSystemStatsHCInForwDatagrams, 1); IPCOM_MIB2_PERIF_U64_ADD(v4, ipIfStatsHCInForwDatagrams, 1, ipnet_netif); ret = ipnet_ip4_pkt_with_iphdr_tx(pkt, IP_NULL); } break; case IPMPLS_EIOXUP_IN: { } break; case IPMPLS_EIOXDOWN_IN: ip_assert(ipnet_netif); ipnet_if_clean(ipnet_netif, IP_TRUE, IP_FALSE); break; case IPMPLS_EIOXDSTADDR_IN: { Ip_u32 dstaddr = ((struct Ip_in_addr *)data)->s_addr; Ip_u32 i; Ipnet_netif *tmpif; struct Ip_ifreq ifreq; struct Ip_sockaddr_in *inaddr = (struct Ip_sockaddr_in *)&ifreq.ip_ifr_addr; dstaddr = ip_htonl(dstaddr); /* * IPNET_NETIF_FOR_EACH sets netif to NULL after the for loop, * this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH(tmpif, i) { if (IP_BIT_ISFALSE(tmpif->ipcom.flags, IP_IFF_POINTOPOINT)) continue; (void)ipnet_if_link_ioctl(tmpif, IP_SIOCGIFDSTADDR, &ifreq); if (inaddr->sin_addr.s_addr == dstaddr) return 1; } } return 0; #endif /* ifdef IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IPMPLS_EIOXINPUT_IN6: ip_assert(ipnet_netif); ipnet_netif->ibytes += pkt->end - pkt->start; ipnet_netif->ipackets++; if (!more) { ret = ipnet_ip6_input(ipnet_netif, data); } else { Ipnet_pkt_ip6 *ip6_hdr; pkt->ipstart = pkt->start; ip6_hdr = (Ipnet_pkt_ip6*) &pkt->data[pkt->ipstart]; --ip6_hdr->hlim; IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_HAS_IP_HDR | IPCOM_PKT_FLAG_FORWARDED); IPCOM_MIB2_SYSWI_U64_ADD(v6, ipSystemStatsHCInForwDatagrams, 1); IPCOM_MIB2_PERIF_U64_ADD(v6, ipIfStatsHCInForwDatagrams, 1, ipnet_netif); ret = ipnet_ip6_tx(IPNET_IP6_GET_CLASS(ip6_hdr), --ip6_hdr->hlim, ip6_hdr->nxt, IPNET_IP6_GET_FLOW(ip6_hdr), (struct Ip_in6_addr*)&ip6_hdr->src, &((struct Ip_sockaddr_in6*)more)->sin6_addr, 0, ipnet_netif->ipcom.ifindex, pkt); } break; case IPMPLS_EIOXUP_IN6: { struct Ipnet_ipv6_key rt6[2]; struct Ipnet_rt_metrics metrics; struct Ipnet_route_add_param param; Ipmpls_eio_in6 *eio_in6 = data; ip_assert(netif); /* Add IPv6 address. */ ret = ipnet_ip6_add_addr(ipnet_netif, &eio_in6->local_addr, 0, IPCOM_ADDR_INFINITE, IPCOM_ADDR_INFINITE, 0); if (ret < 0) { IP_PANIC2(); return ret; } /* Add cloning route for link local net. */ ipcom_memset(¶m, 0, sizeof(struct Ipnet_route_add_param)); ipcom_memset(&metrics, 0, sizeof(struct Ipnet_rt_metrics)); ipcom_memset(rt6, 0, sizeof(struct Ipnet_ipv6_key) * 2); rt6[0].scope_id = netif->ifindex; rt6[0].addr.in6.addr8[0] = 0xfe; rt6[0].addr.in6.addr8[1] = 0x80; rt6[1].scope_id = 0xffffffff; rt6[1].addr.in6.addr32[0] = 0xffffffff; rt6[1].addr.in6.addr32[1] = 0xffffffff; param.domain = IP_AF_INET6; param.vr = ipnet_netif->vr_index; param.table = IPCOM_ROUTE_TABLE_DEFAULT; param.netif = ipnet_netif; param.key = &rt6[0]; param.netmask = &rt6[1]; param.flags = IPNET_RTF_UP | IPNET_RTF_MASK | IPNET_RTF_CLONING | IPNET_RTF_DONE; param.metrics = &metrics; metrics.rmx_hopcount = netif->metric; metrics.rmx_expire = IPCOM_ADDR_INFINITE; ret = ipnet_route_add(¶m); if (ret < 0) { IP_PANIC2(); return ret; } /* Add link local multicast route. */ rt6[0].addr.in6.addr8[0] = 0xff; rt6[0].addr.in6.addr8[1] = 0x02; param.flags = IPNET_RTF_UP | IPNET_RTF_MASK | IPNET_RTF_DONE; ret = ipnet_route_add(¶m); if (ret < 0) { IP_PANIC2(); return ret; } /* Add IPv6 host route to peer. */ ipcom_memcpy(&rt6[0].addr, &eio_in6->peer_addr, sizeof(struct Ip_in6_addr)); param.netmask = IP_NULL; param.flags = IPNET_RTF_UP | IPNET_RTF_HOST | IPNET_RTF_DONE; ret = ipnet_route_add(¶m); if (ret < 0) { IP_PANIC2(); return ret; } } break; case IPMPLS_EIOXDOWN_IN6: ip_assert(ipnet_netif); ipnet_if_clean(ipnet_netif, IP_FALSE, IP_TRUE); break; #endif /* ifdef IPCOM_USE_INET6 */ case IPMPLS_EIOXADDETHHDR: ipnet_eth_add_hdr((Ipcom_pkt*) data, ipnet_netif, (const Ip_u8*)more, ipnet_netif->ipcom.link_addr, IPNET_ETHFRAME_MPLS_UNICAST); ret = 0; break; case IPMPLS_EIOXFLUSHSHORTCUTROUTES: ipcom_hash_for_each(ipnet->rtabs, (Ipcom_hash_foreach_cb_func) ipnet_mpls_for_each_rtab_cb, data); break; #ifdef IPCOM_USE_INET case IPMPLS_EIOXARPPROBE: if (penultimate == IP_NULL || *penultimate == IP_FALSE) pkt->force_frame_type = IPNET_ETHFRAME_MPLS_UNICAST; ret = ipnet_mpls_arp_probe((Ipnet_netif *) netif, pkt, (struct Ip_sockaddr_in *) more, ipnet_mpls_probe_reply); break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IPMPLS_EIOXNDPROBE: if (penultimate == IP_NULL || *penultimate == IP_FALSE) pkt->force_frame_type = IPNET_ETHFRAME_MPLS_UNICAST; ipnet_mpls_ip6_neighbor_cache_probe((Ipnet_netif *) netif, pkt, (struct Ip_sockaddr_in6 *) more, ipnet_mpls_probe_reply); break; #endif /* IPCOM_USE_INET6 */ default: ip_assert(ipnet_netif); ipnet_kioevent(ipnet_netif, command, data, IP_FLAG_FC_STACKCONTEXT); break; } return ret; } #endif /* IPMPLS */ /* **************************************************************************** * END OF FILE **************************************************************************** */