/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_ioctl.c,v $ $Revision: 1.248.10.27 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_ioctl.c,v $ * $Author: csun $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * ipcom_socketioctl() function. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #define IPCOM_SKIP_NATIVE_SOCK_API #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include "ipnet.h" #include "ipnet_classifier.h" #include "ipnet_h.h" #include "ipnet_loopback.h" #include "ipnet_route.h" #include "ipnet_usr_sock.h" #ifdef IPNET_USE_TUNNEL #include "ipnet_tunnel.h" #endif #if defined(IPNET_USE_VLAN) || defined(IPNET_USE_VLAN_STACKING) #include "ipnet_eth.h" #endif #ifdef IPNET_USE_VLAN_STACKING #include "ipnet_svlan.h" #endif #ifdef IPNET_USE_PCAP #include "ipnet_pcap.h" #endif #ifdef IPTCP #include #endif #ifdef IPMPLS #include #endif #ifdef IPMIP4 #include #endif #ifdef IPIPSEC2 #include #include #endif #ifdef IPCOM_WV_INSTRUMENTATION #include #endif #ifdef IPROHC #include #endif #ifdef IPNET_USE_SECY #include "ipmacsec.h" #endif #ifdef IPBRIDGE #include "ipbridge.h" #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ struct Ipnet_ifconf_entry { char if_name[IP_IFNAMSIZ]; union ifconf_addr { struct Ip_sockaddr sa; struct Ip_sockaddr_dl sdl; struct Ip_sockaddr_in sin; #ifdef IPCOM_USE_INET6 struct Ip_sockaddr_in6 sin6; #endif } s; }; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ #ifdef IPIPSEC2 IP_GLOBAL Ip_err ipipsec_conf_internal(Ip_tag *ctrl_tags); IP_GLOBAL Ip_err ipipsec_ctrl_internal(Ip_tag *ctrl_tags); IP_GLOBAL Ip_err ipipsec_sactrl_internal(Ipipsec_sa_handle *sah, Ip_tag *ctrl_tags); #endif /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_ioctl_sigalarm *=========================================================================== * Description: Raises EINTR error on all socket owned by the task with PID * equal to *ppid. * Parameters: ppid - Points to the PID of the application to interrupt * or IP_NULL if the EINTR should be cleared. * Returns: * */ IP_STATIC void ipnet_ioctl_sigalarm(Ip_pid_t *ppid) { Ipnet_socket *sock; int i; Ip_pid_t pid; if (ppid == IP_NULL) pid = ipnet_get_effective_pid(); else pid = *ppid; for (i = 0; i < ipnet_conf_max_sockets; i++) { sock = ipnet->sockets[i]; if (sock != IP_NULL && sock->ipcom.pid == pid && (sock->ipcom.so_errno == 0 || sock->ipcom.so_errno == IP_ERRNO_EINTR)) { if (ppid == IP_NULL) sock->ipcom.so_errno = 0; else ipnet_sock_data_avail(sock, IP_ERRNO_EINTR, 0); } } ipcom_free(ppid); } /* *=========================================================================== * ipnet_ioctl_if_is_rt_affected *=========================================================================== * Description: Checks if the specified route is affected when the interface * change route table. * Parameters: netif - The network interface. * rt - The route to test. * Returns: IP_TRUE - Route is affected. * IP_FALSE - Route is not affected. * */ IP_STATIC Ip_bool ipnet_ioctl_if_is_rt_affected(Ipnet_netif *netif, Ipnet_route_entry *rt) { if (rt->netif == IP_NULL) return IP_FALSE; if (rt->netif->ipcom.ifindex == netif->ipcom.ifindex) return IP_TRUE; #ifdef IPCOM_USE_INET if (IPNET_ROUTE_GET_FAMILY(rt->head) == IP_AF_INET) { struct Ipnet_ipv4_key *ipv4_key = (struct Ipnet_ipv4_key *) rt->hdr.key; if (ipnet_ip4_get_addr_type2(ipv4_key->addr.s_addr, netif) == IPNET_ADDR_TYPE_UNICAST) return IP_TRUE; } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (IPNET_ROUTE_GET_FAMILY(rt->head) == IP_AF_INET6) { struct Ipnet_ipv6_key *ipv6_key = (struct Ipnet_ipv6_key *) rt->hdr.key; if (ipnet_ip6_get_addr_type2(&ipv6_key->addr, netif) == IPNET_ADDR_TYPE_UNICAST && (ipnet_ip6_is_scoped_addr(&ipv6_key->addr) == IP_FALSE || ipv6_key->scope_id == ipnet_ip6_get_zone(netif->ipcom.ifindex, &ipv6_key->addr))) return IP_TRUE; } #endif /* IPCOM_USE_INET6 */ return IP_FALSE; } /* *=========================================================================== * ipnet_ioctl_if_remove_routes *=========================================================================== * Description: Removes all routes that uses the specified interface. * Parameters: netif - The network interface. * vr - The route table from which the routes should be removed. * Returns: * */ IP_STATIC void ipnet_ioctl_if_remove_routes(Ipnet_netif *netif, Ip_u16 vr) { Ipnet_route_entry *current; int domain[2] = { 0, 0 }; unsigned num_domains = 0; unsigned i, t; unsigned num_tables = 0; Ip_u32 *table_ids; #ifdef IPCOM_USE_INET domain[num_domains++] = IP_AF_INET; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 domain[num_domains++] = IP_AF_INET6; #endif /* IPCOM_USE_INET */ if (ipnet_route_get_table_ids(vr, IP_NULL, &num_tables) < 0) { /* Can only happen if there is a bug in the code */ IP_PANIC(); return; } table_ids = ipcom_calloc(num_tables, sizeof(*table_ids)); if (table_ids == IP_NULL) { IP_PANIC2(); return; } if (ipnet_route_get_table_ids(vr, table_ids, &num_tables) < 0) { /* Can only happen if there is a bug in the code */ IP_PANIC(); goto out; } for (i = 0; i < num_domains; i++) for (t = 0; t < num_tables; t++) { current = ipnet_route_first_entry(domain[i], vr, table_ids[t]); while (current != IP_NULL) { if (ipnet_ioctl_if_is_rt_affected(netif, current) == IP_FALSE) { current = ipnet_route_next_entry(current); continue; } (void)ipnet_route_delete2(domain[i], vr, IPNET_ROUTE_GET_TABLE(current->head), current->hdr.key, current->hdr.mask, current->gateway, current->netif->ipcom.ifindex, 0, 0, IP_FALSE); current = ipnet_route_first_entry(domain[i], vr, table_ids[t]); } } out: ipcom_free(table_ids); } /* *=========================================================================== * ipnet_ioctl_if_queue *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_ioctl_if_queue(unsigned long request, struct Ipnet_ifqueue *ifq, Ipnet_netif *netif) { Ipnet_pkt_queue *q; switch (request) { case IPNET_SIOCGIFQUEUE: if (netif->snd_queue == IP_NULL) { ifq->ifq_id = IP_IFQ_ID_DEFAULT; ifq->ifq_parent_id = IP_IFQ_ID_NONE; ipcom_strncpy(ifq->ifq_type, "none", sizeof(ifq->ifq_type)); } else { q = netif->snd_queue; if (ifq->ifq_id != IP_IFQ_ID_NONE && ifq->ifq_id != q->id) { if (q->c_ops == IP_NULL) return -IP_ERRNO_ESRCH; q = q->c_ops->q_get(q, ifq->ifq_id); if (q == IP_NULL) return -IP_ERRNO_ESRCH; } ifq->ifq_id = q->id; ifq->ifq_parent_id = q->parent_id; ipcom_strncpy(ifq->ifq_type, q->type, sizeof(ifq->ifq_type)); q->dump(q, &ifq->ifq_data); ifq->ifq_count = q->count(q); } break; case IPNET_SIOCSIFQUEUE: ipnet_if_clean_snd_queue(netif); if (ipcom_strcmp(ifq->ifq_type, "none") == 0) { ipnet_pkt_queue_delete(netif->snd_queue); netif->snd_queue = IP_NULL; } else { int ret; ret = ipnet_pkt_queue_new(ifq, netif, &q); if (ret < 0) return ret; } break; default: IP_PANIC(); return -IP_ERRNO_EINVAL; } return 0; } /* *=========================================================================== * ipnet_ioctl_if_queue_filter *=========================================================================== * Description: Controls IPNET interface queue filters for classfull interface * queues. * Parameters: request - request code * f - filter parameters * netif - the interface the queue is installed at * Returns: * */ IP_STATIC int ipnet_ioctl_if_queue_filter(unsigned long request, struct Ipnet_ifqueue_filter *f, Ipnet_netif *netif) { Ipnet_pkt_queue *q = netif->snd_queue; Ipnet_pkt_queue *child_q; if (q == IP_NULL || q->c_ops == IP_NULL) { IPCOM_LOG3(WARNING, "ioctl: Could not set filter since queue %s:%d at %s does not support that operation", q ? q->type : "none", q ? q->id : 0, netif->ipcom.name); return -IP_ERRNO_EINVAL; } q = q->c_ops->q_get(q, f->filter_queue_id); if (q == IP_NULL) { IPCOM_LOG2(WARNING, "ioctl: did not find a queue with ID: %d at interface %s", f->filter_queue_id, netif->ipcom.name); return -IP_ERRNO_ESRCH; } if (q->c_ops == IP_NULL) { IPCOM_LOG3(WARNING, "ioctl: queue %s:%d at %s is not a container", q->type, q->id, netif->ipcom.name); return -IP_ERRNO_EINVAL; } switch (request) { case IPNET_SIOCXAIFQFILTER: child_q = q->c_ops->q_get(q, f->filter_child_queue_id); if (child_q == IP_NULL || child_q->parent_id != q->id) { IPCOM_LOG3(WARNING, "ioctl: queue %s:%d has no child with ID %d", q->type, q->id, f->filter_child_queue_id); return -IP_ERRNO_EINVAL; } return q->c_ops->f_insert(q, f->filter_id, &f->filter_rule, f->filter_child_queue_id); case IPNET_SIOCXDIFQFILTER: return q->c_ops->f_remove(q, f->filter_id); default: IP_PANIC(); break; } return -IP_ERRNO_EINVAL; } /* *=========================================================================== * ipnet_ioctl_if_siocgifconf *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_ioctl_if_siocgifconf(Ipnet_socket *sock, struct Ip_ifconf *ifconf) { Ipnet_netif *netif; int len, ifc_len; Ip_u32 i; struct Ipnet_ifconf_entry *entry; entry = (struct Ipnet_ifconf_entry *)ifconf->ip_ifc_buf; ifc_len = 0; /* * IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for * loop, this is a performance optimized defination */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH_ON_VR(netif, sock->vr_index, i) { /* sockaddr_dl */ len = sizeof(entry->if_name) + sizeof(struct Ip_sockaddr_dl); if ((ifconf->ifc_len - ifc_len) < len) break; ifc_len += len; ipcom_memset(entry, 0, (Ip_size_t)len); ipcom_strcpy(entry->if_name, netif->ipcom.name); IPCOM_SA_LEN_SET(&entry->s.sdl, sizeof(struct Ip_sockaddr_dl)); entry->s.sdl.sdl_family = IP_AF_LINK; entry->s.sdl.sdl_index = (Ip_u16) netif->ipcom.ifindex; entry->s.sdl.sdl_type = (Ip_u8) netif->ipcom.type; switch (netif->ipcom.type) { #ifdef IPCOM_USE_ETHERNET case IP_IFT_ETHER: entry->s.sdl.sdl_type = IP_IFT_ETHER; entry->s.sdl.sdl_alen = 6; ipcom_memcpy(entry->s.sdl.sdl_data, netif->ipcom.link_addr, 6); break; #endif #ifdef IPNET_USE_WLAN case IP_IFT_IEEE80211: entry->s.sdl.sdl_type = IP_IFT_IEEE80211; entry->s.sdl.sdl_alen = 6; ipcom_memcpy(entry->s.sdl.sdl_data, netif->ipcom.link_addr, 6); break; #endif #ifdef IPNET_USE_VLAN case IP_IFT_L2VLAN: entry->s.sdl.sdl_type = IP_IFT_L2VLAN; entry->s.sdl.sdl_alen = 6; ipcom_memcpy(entry->s.sdl.sdl_data, netif->ipcom.link_addr, 6); break; #endif default: break; } entry = (struct Ipnet_ifconf_entry *)((char*)entry + len); /* sockaddr_in */ #ifdef IPCOM_USE_INET { Ipnet_ip4_addr_entry *inet4_addr; /* List of IPv4 addresses */ for (inet4_addr = netif->inet4_addr_list; inet4_addr; inet4_addr = inet4_addr->next) { if (inet4_addr->type != IPNET_ADDR_TYPE_UNICAST) continue; len = sizeof(entry->if_name) + sizeof(struct Ip_sockaddr_in); if ((ifconf->ifc_len - ifc_len) < len) break; /* Add the unicast address */ ifc_len += len; ipcom_memset(entry, 0, (Ip_size_t)len); ipcom_strcpy(entry->if_name, netif->ipcom.name); entry->s.sin.sin_family = IP_AF_INET; IPCOM_SA_LEN_SET(&entry->s.sin, sizeof(struct Ip_sockaddr_in)); entry->s.sin.sin_addr.s_addr = inet4_addr->ipaddr_n; entry = (struct Ipnet_ifconf_entry *)((char*)entry + len); } } #endif /* IPCOM_USE_INET */ /* sockaddr_in6 */ #ifdef IPCOM_USE_INET6 { Ipnet_ip6_addr_entry *inet6_addr; /* List of IPv6 addresses */ for (inet6_addr = netif->inet6_addr_list; inet6_addr; inet6_addr = inet6_addr->next) { if (IP_IN6_IS_ADDR_MULTICAST(&inet6_addr->addr) || IP_BIT_ISSET(inet6_addr->flags, IPNET_IP6_ADDR_FLAG_ANYCAST)) continue; len = sizeof(entry->if_name) + sizeof(struct Ip_sockaddr_in6); if ((ifconf->ifc_len - ifc_len) < len) break; ifc_len += len; ipcom_memset(entry, 0, (Ip_size_t)len); ipcom_strcpy(entry->if_name, netif->ipcom.name); entry->s.sin.sin_family = IP_AF_INET6; IPCOM_SA_LEN_SET(&entry->s.sin6, sizeof(struct Ip_sockaddr_in6)); if(IP_IN6_IS_ADDR_LINK_LOCAL(&inet6_addr->addr)) entry->s.sin6.sin6_scope_id = netif->ipcom.ifindex; ipcom_memcpy(&entry->s.sin6.sin6_addr, &inet6_addr->addr, sizeof(struct Ip_in6_addr)); entry = (struct Ipnet_ifconf_entry *)((char*)entry + len); } } #endif /* IPCOM_USE_INET6 */ } ifconf->ifc_len = ifc_len; return 0; } /* *=========================================================================== * ipnet_ioctl_rtab *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_ioctl_rtab(unsigned long request, void *argp) { Ip_u16 *vr_ptr = argp; struct Ip_sioc_route_table *srt = argp; int ret; switch (request) { case IP_SIOCADDVR: ret = ipnet_route_add_table(*vr_ptr, IPCOM_ROUTE_TABLE_DEFAULT, vr_ptr, IP_NULL); return ret; case IP_SIOCDELVR: return ipnet_route_delete_vr(*vr_ptr); case IP_SIOCADDROUTETAB: return ipnet_route_add_table(srt->vr, srt->table, &srt->vr, &srt->table); case IP_SIOCDELROUTETAB: if (IPCOM_ROUTE_TABLE_IS_DEFAULT(srt->table)) { IPCOM_LOG0(WARNING, "Use IP_SIOCDELVR to delete a virtual router"); return -IP_ERRNO_EACCES; } return ipnet_route_delete_table(srt->vr, srt->table); #ifdef IPNET_USE_ROUTE_TABLE_NAMES case IP_SIOCGETROUTETAB: if (ipnet_route_vr_and_table_from_name(srt->name, &srt->vr, &srt->table) == 0) return 0; ret = ipnet_ioctl_rtab(IP_SIOCADDROUTETAB, srt); if (ret < 0) return ret; return ipnet_ioctl_rtab(IP_SIOCSROUTETABNAME, srt); case IP_SIOCSROUTETABNAME: return ipnet_route_set_name(srt->vr, srt->table, srt->name); case IP_SIOCGROUTETABNAME: return ipnet_route_get_name(srt->vr, srt->table, srt->name) == IP_NULL ? -IP_ERRNO_ESRCH : 0; #endif /* IPNET_USE_ROUTE_TABLE_NAMES */ default: return - IP_ERRNO_EINVAL; } } /* *=========================================================================== * ipnet_register_if_attach_handler *=========================================================================== * Description: Registers a new network interface attach handler. * Parameters: f - function that knows how to attach network interfaces * Returns: * */ IP_GLOBAL void ipnet_register_if_attach_handler(Ipnet_if_attach_handler f) { ipnet->if_attach_handler = f; } /* *=========================================================================== * ipnet_ioctl_if_attach *=========================================================================== * Description: * Parameters: ifreq - * Returns: * */ IP_STATIC int ipnet_ioctl_if_attach(struct Ip_ifreq *ifreq) { if (ipnet->if_attach_handler) return ipnet->if_attach_handler(ifreq->ifr_name, ifreq->ifr_ifru.ifru_newname); return -IP_ERRNO_EOPNOTSUPP; } /* *=========================================================================== * ipnet_ioctl_if *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_ioctl_if(Ipnet_socket *sock, unsigned long request, struct Ip_ifreq *ifreq) { Ipnet_netif *netif; int ret = 0; if (request != IP_SIOCGIFCONF) { ip_assert(IP_IOCPARM_LEN(request) >= IP_IFNAMSIZ); /* Ensure that the interface name is terminated */ ifreq->ifr_name[IP_IFNAMSIZ-1] = '\0'; } switch (request) { case IP_SIOCSIFNAME: case IP_SIOCSIFHWBROADCAST: return -IP_ERRNO_EPERM; case IP_SIOCGIFCONF: return ipnet_ioctl_if_siocgifconf(sock, (struct Ip_ifconf *)ifreq); case IP_SIOCIFCREATE: case IP_SIOCXATTACH: if (ipnet_if_nametonetif(sock->vr_index, ifreq->ifr_name)) return -IP_ERRNO_EEXIST; if (request == IP_SIOCXATTACH) return ipnet_ioctl_if_attach(ifreq); #ifdef IPNET_USE_LOOPBACK if (ipcom_strncmp(ifreq->ifr_name, "lo", 2) == 0) { ret = ipnet_loopback_attach(sock->vr_index, ifreq->ifr_name); return ret; } #endif #ifdef IPNET_USE_VLAN if (ipcom_strncmp(ifreq->ifr_name, "vlan", 4) == 0) return ipnet_vlan_create(sock, ifreq); if (ipcom_strchr(ifreq->ifr_name, '.') != IP_NULL) return ipnet_vlan_compact_create(sock, ifreq); #endif #ifdef IPPPP #ifdef IPPPP_USE_PPPOE if (ipcom_strncmp(ifreq->ifr_name, "pppoe", 5) == 0) { if (ipnet->ppp.pppoe_create == IP_NULL) return -IP_ERRNO_EINVAL; ifreq->ip_ifr_vr = sock->vr_index; return ipnet->ppp.pppoe_create(ifreq); } #endif #ifdef IPPPP_USE_PPPL2TP if (ipcom_strncmp(ifreq->ifr_name, "pppl2tp", 7) == 0) { if (ipnet->ppp.pppl2tp_create == IP_NULL) return -IP_ERRNO_EINVAL; return ipnet->ppp.pppl2tp_create(ifreq); } #endif if (ipcom_strncmp(ifreq->ifr_name, "ppp", 3) == 0) { if (ipnet->ppp.if_create == IP_NULL) return -IP_ERRNO_EINVAL; return ipnet->ppp.if_create(ifreq); } #endif #ifdef IPNET_USE_TUNNEL return ipnet_tunnel_create_bsd(sock, ifreq); #else return -IP_ERRNO_EINVAL; #endif case IP_X_SIOCADDTUNNEL: #ifdef IPNET_USE_TUNNEL /**/ return ipnet_tunnel_create_linux(sock, ifreq); #else return -IP_ERRNO_EINVAL; #endif case IP_SIOCGIFNAME: netif = ipnet_if_indextonetif(sock->vr_index, (Ip_u32) ifreq->ifr_ifru.ifru_ifindex); if (netif == IP_NULL) return -IP_ERRNO_ENXIO; ipcom_strncpy(ifreq->ifr_name, netif->ipcom.name, sizeof(ifreq->ifr_name)); return ret; default: break; } netif = ipnet_if_nametonetif(sock->vr_index, ifreq->ifr_name); if (netif == IP_NULL) return -IP_ERRNO_ENXIO; switch (request) { case IP_SIOCGIFINDEX: ifreq->ifr_ifru.ifru_ifindex = (int) netif->ipcom.ifindex; break; case IP_SIOCGIFMTU: ifreq->ifr_ifru.ifru_mtu = (int)netif->ipcom.mtu; break; case IP_SIOCSIFMTU: ret = ipnet_if_link_ioctl(netif, (Ip_u32) request, ifreq); if (ret == 0 || ret == -IP_ERRNO_EOPNOTSUPP || ret == -IP_ERRNO_EINVAL) { /* * ioctl either successful, or not supported by the link * layer (IP_ERRNO_EOPNOTSUPP) or by the driver * (IP_ERRNO_EINVAL). The 2 errors are accepted since that * is how setting the MTU used to work, i.e. no * notification to the driver at all. * The driver has to be notifed on VxWorks to enable * Ethernet jumbograms (9k frames). */ netif->ipcom.mtu = (Ip_u32) ifreq->ifr_ifru.ifru_mtu; #ifdef IPBRIDGE (void)ipbridge_mtu_change_notify(netif); #endif ret = 0; } ipnet_if_update_mib2_ifLastChange(netif); break; case IP_SIOCGIFFLAGS: ifreq->ifr_ifru.ifru_flags = (Ip_u16)netif->ipcom.flags; break; #ifdef IP_PORT_LKM case IP_X_SIOCGIFLINKFLAGS: /* LKM/LAS specific ioctl used for setting the link 0|1|2 flags */ { Ip_u32 link_flags = netif->ipcom.flags; link_flags &= IP_IFF_LINK0 | IP_IFF_LINK1 | IP_IFF_LINK2; ifreq->ifr_ifru.ifru_flags = (Ip_u16)(link_flags >> 16); } break; #endif #ifdef IPCOM_USE_HW_CAPS case IP_SIOCGIFCAP: ifreq->ip_ifr_reqcap = 0; /* currently not supported */ ifreq->ip_ifr_curcap = netif->ipcom.drv_capabilities; break; #endif /* IPCOM_USE_HW_CAPS */ case IP_SIOCGIFMETRIC: ifreq->ifr_ifru.ifru_metric = (int) netif->ipcom.metric; break; case IP_SIOCSIFMETRIC: netif->ipcom.metric = (Ip_u32) ifreq->ifr_ifru.ifru_metric; ipnet_if_update_mib2_ifLastChange(netif); break; case IP_SIOCSIFVR: ret = ipnet_ioctl_move_if_rt_to_vr(netif, ifreq->ip_ifr_vr); ipnet_if_update_mib2_ifLastChange(netif); break; case IP_SIOCGIFVR: ifreq->ip_ifr_vr = netif->vr_index; break; case IP_SIOCSIFFLAGS: ret = ipnet_netif_change_flags(&netif, ifreq->ifr_ifru.ifru_flags, 0); ipnet_if_update_mib2_ifLastChange(netif); break; #ifdef IP_PORT_LKM case IP_X_SIOCSIFLINKFLAGS: /* LKM/LAS specific ioctl used for setting the link 0|1|2 flags */ { Ip_u32 link_flags = ifreq->ifr_ifru.ifru_flags << 16; link_flags &= IP_IFF_LINK0 | IP_IFF_LINK1 | IP_IFF_LINK2; IP_BIT_CLR(netif->ipcom.flags, IP_IFF_LINK0 | IP_IFF_LINK1 | IP_IFF_LINK2); IP_BIT_SET(netif->ipcom.flags, link_flags); } break; #endif case IPNET_SIOCGIFQUEUE: case IPNET_SIOCSIFQUEUE: return ipnet_ioctl_if_queue(request, (struct Ipnet_ifqueue *) ifreq, netif); case IPNET_SIOCXAIFQFILTER: case IPNET_SIOCXDIFQFILTER: return ipnet_ioctl_if_queue_filter(request, (struct Ipnet_ifqueue_filter *) ifreq, netif); case IP_SIOCXDETACH: if (!ipnet_if_can_detach(&netif->ipcom)) return -IP_ERRNO_EBUSY; /* Detach IPNET interface */ ret = ipnet_if_detach(netif); if (ret < 0) return ret; /* Call link layer detach */ ret = ipnet_if_link_ioctl(netif, IP_SIOCXDETACH, IP_NULL); /* * One extra interface unlock is the same as calling * ipcom_if_free(). This must be done regardless of what * ipnet_if_link_ioctl() returns since the interface has * already been detached from the stack. */ IPNET_IF_UNLOCK(netif); break; case IP_SIOCXRESETSTAT: ipcom_memset((Ip_u8 *) netif + ip_offsetof(Ipnet_netif, ipackets), 0, ip_offsetof(Ipnet_netif, noproto) - ip_offsetof(Ipnet_netif, ipackets)); break; case IP_SIOCXGDHCPRUNNING: ifreq->ifr_ifru.ifru_opt = IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_X_DHCPRUNNING) ? 1 : 0; break; case IP_SIOCXSDHCPRUNNING: if (ifreq->ifr_ifru.ifru_opt == 1) IP_BIT_SET(netif->ipcom.flags, IP_IFF_X_DHCPRUNNING); else IP_BIT_CLR(netif->ipcom.flags, IP_IFF_X_DHCPRUNNING); ipnet_kioevent(netif, IP_EIOXCHANGE, IP_NULL, IP_FLAG_FC_STACKCONTEXT); break; #ifdef IPNET_USE_PCAP case IP_SIOCXPCAP: return ipnet_pcap_ioctl(netif, (Ip_u32) request, ifreq); #endif case IP_SIOCGIFTXQLEN: #if !defined(IP_PORT_LKM) ifreq->ifr_ifru.ifru_ivalue = 0; break; #endif case IP_SIOCSIFTXQLEN: return -IP_ERRNO_EPERM; #ifdef IPNET_USE_VLAN_STACKING case IP_SIOCSETVLANPVID: case IP_SIOCSETVLANP: case IP_SIOCDELVLANP: case IP_SIOCSETVLANPRIMAP: case IP_SIOCSETVLANINRULE: case IP_SIOCSETVLANOUTRULE: case IP_SIOCGETPVLAN: return ipnet_vlan_stacking_ioctl(request, ifreq, netif); #endif /* IPNET_USE_VLAN_STACKING */ #ifdef IPNET_USE_SECY case IP_SIOCXSECY: if (netif->secy && netif->secy->ioctl) ret = netif->secy->ioctl(netif, (Ip_u32)request, ifreq); else ret = -IP_ERRNO_EOPNOTSUPP; break; #endif default: /* Remaining ioctl()'s to to link layer (including ETH, PPP etc). */ ret = ipnet_if_link_ioctl(netif, (Ip_u32 )request, ifreq); break; } return ret; } /* *=========================================================================== * ipnet_ioctl_wlan *=========================================================================== * Description: * Parameters: * Returns: * */ #ifdef IPCOM_USE_WLAN IP_STATIC int ipnet_ioctl_wlan(Ipnet_socket *sock, unsigned long request, struct Ip_ifreq *ifreq) { Ipcom_netif *netif; IpcomWlanBssReq *bssreq; IpcomWlanBss *bsslist; int size; IpcomWlanStatus ret; int i; netif = (Ipcom_netif *) ipnet_if_nametonetif(sock->vr_index, ifreq->ifr_name); if (netif == IP_NULL || netif->wlan == IP_NULL) return IP_ERRNO_ENODEV; switch (request) { case IP_SIOCXGWLANLIST: bssreq = (IpcomWlanBssReq *) ifreq->ip_ifr_data; ret = ipcom_wlan_get_bsslist(netif, &bsslist, &size); if (ret != IPCOM_WLAN_STATUS_SUCCESS) return IPCOM_ERR_FAILED; /* What about user/kernel space copying here? */ size = IP_MIN(size, IPCOM_WLAN_MAX_BSS); for (i = 0; i < size; i++) bssreq->bsslist[i] = bsslist[i]; bssreq->num_bss = size; break; default: break; } } #endif /* IPCOM_USE_WLAN */ /* *=========================================================================== * ipcom_ioctl_length_of_all_pkts *=========================================================================== * Description: Returns the sum of the length of all packets in the list. * Parameters: head - the head of the packet list * Returns: * */ IP_STATIC int ipcom_ioctl_length_of_all_pkts(const Ipcom_pkt *head) { int totlen = 0; const Ipcom_pkt *pkt; for (pkt = head; pkt != IP_NULL; pkt = pkt->next) totlen += ipcom_pkt_get_length(pkt); return totlen; } /* *=========================================================================== * ipnet_ioctl_sock *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_ioctl_sock(Ipnet_socket *sock, unsigned long request, void *argp) { int *iargp = (int*) argp; unsigned int *uargp = (unsigned int*) argp; Ip_pid_t *pid; switch (request) { case IP_SIOCSPGRP: sock->ipcom.pid = *uargp; break; case IP_SIOCGPGRP: *uargp = sock->ipcom.pid; break; case IP_FIONBIO: if (*iargp) IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING); else IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING); #ifdef IPNET_SOCK_SENDMSG_ASYNCHRONOUS_IN_NONBLOCK_MODE /* FALL THROUGH */ #else break; #endif case IP_SIOCSNDASYNC: if (*iargp) IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS); else { int oldflags = sock->flags; IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS); if (IP_BIT_ISSET(oldflags, IPNET_SOCKET_FLAG_SENDMSG_ASYNCHRONOUS)) ipnet_sock_make_next_sendmsg_synchronous(sock); } break; case IP_X_FIOGNBIO: *iargp = IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING) ? 1 : 0; break; case IP_FIONREAD: if (sock->proto != IP_IPPROTO_TCP) { /* * Returns the size of the next pending datagram in the * integer in bytes, or 0 when no datagram is pending. */ *iargp = ipcom_pkt_get_length(sock->rcv_head); break; } /* fall through */ case IP_SIOCINQ: /* * Returns the total size of all queued packet. */ *iargp = ipcom_ioctl_length_of_all_pkts(sock->rcv_head); break; case IP_SIOCOUTQ: *iargp = ipcom_atomic_get(&sock->snd_bytes); break; case IP_X_SIOCSINTR: if (!sock->ipcom.pid) { return -IP_ERRNO_EINVAL; } pid = ipcom_malloc(sizeof(*pid)); if (!pid) { return -IP_ERRNO_ENOMEM; } *pid = sock->ipcom.pid; if (ipnet_timeout_schedule((Ip_s64) *iargp * 1000, (Ipnet_timeout_handler) ipnet_ioctl_sigalarm, pid, IP_NULL) != 0) { ipcom_free(pid); return -IP_ERRNO_ENOMEM; } break; case IP_X_SIOCGINTR: ipnet_ioctl_sigalarm(IP_NULL); break; #ifdef IP_X_SIOCSDEBUG case IP_X_SIOCSDEBUG: ipnet->debug = *iargp; break; #endif #ifdef IP_X_SIOCGDEBUG case IP_X_SIOCGDEBUG: *iargp = ipnet->debug; break; #endif default: return -IP_ERRNO_EINVAL; } return 0; } #ifdef IPCOM_USE_INET /* *=========================================================================== * ipnet_ioctl_netif_from_in_addr *=========================================================================== * Description: Returns the interface for which * Parameters: request - IP_SIOCADDRT or IP_SIOCDELRT for add or delete a * IPv4 route. * Returns: 0 = success, <0 = error * */ IP_STATIC Ipnet_netif * ipnet_ioctl_netif_from_in_addr(Ipnet_socket *sock, Ip_u32 in_addr_n, Ip_u32 in_mask_n) { Ip_u32 i; Ipnet_ip4_addr_entry *addr; Ipnet_netif *netif; /* * IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for loop, * this is a performance optimized definition */ /* coverity[result_independent_of_operands] */ IPNET_NETIF_FOR_EACH_ON_VR(netif, sock->vr_index, i) { for (addr = netif->inet4_addr_list; addr != IP_NULL; addr = addr->next) { if (((addr->ipaddr_n ^ in_addr_n) & in_mask_n) == 0) return netif; } } return IP_NULL; } /* *=========================================================================== * ipnet_ioctl_ip4_bsd43_compat *=========================================================================== * Description: Adds or deletes route. Provides compatibility with application * that still uses the old BSD 4.3 API. * Parameters: request - IP_SIOCADDRT or IP_SIOCDELRT for add or delete a * IPv4 route. * Returns: 0 = success, <0 = error * */ IP_STATIC int ipnet_ioctl_ip4_bsd43_compat(Ipnet_socket *sock, unsigned long request, struct Ip_ortentry *ort) { struct Ipnet_route_add_param param; struct Ipnet_ipv4_key key; struct Ipnet_ipv4_key mask; struct Ipnet_ipv4_key gw; Ipnet_route_entry *re; struct Ip_sockaddr *del_gw = IP_NULL; if (ort->rt_dst.sa_family != IP_AF_INET) return -IP_ERRNO_EINVAL; key.addr.s_addr = ((struct Ip_sockaddr_in *) &ort->rt_dst)->sin_addr.s_addr; if (request == IP_SIOCDELRT && IP_BIT_ISSET(ort->rt_flags, IPNET_RTF_GATEWAY)) { if (ort->rt_gateway.sa_family != IP_AF_INET) return -IP_ERRNO_EINVAL; del_gw = ((struct Ip_sockaddr *) &ort->rt_gateway); } mask.addr.s_addr = IP_NETMASK(key.addr.s_addr); if (request == IP_SIOCDELRT) return ipnet_route_delete2(IP_AF_INET, sock->vr_index, IPCOM_ROUTE_TABLE_DEFAULT, &key, &mask, del_gw, 0, ipnet_get_effective_pid(), 0, IP_FALSE); ipcom_memset(¶m, 0, sizeof(param)); param.domain = IP_AF_INET; param.table = IPCOM_ROUTE_TABLE_DEFAULT; param.flags = ort->rt_flags; param.vr = sock->vr_index; if (IP_BIT_ISSET(param.flags, IPNET_RTF_GATEWAY)) { if (ort->rt_gateway.sa_family != IP_AF_INET) return -IP_ERRNO_EINVAL; gw.addr.s_addr = ((struct Ip_sockaddr_in *) &ort->rt_gateway)->sin_addr.s_addr; param.gateway = &ort->rt_gateway; if (ipnet_route_raw_lookup(IP_AF_INET, sock->vr_index, IPCOM_ROUTE_TABLE_DEFAULT, IPNET_RTL_FLAG_DONTCLONE, &gw, 0, 0, &re) < 0) return -IP_ERRNO_ENETUNREACH; if (IP_BIT_ISSET(re->netif->ipcom.flags, IP_IFF_LOOPBACK)) param.netif = ipnet_ioctl_netif_from_in_addr(sock, gw.addr.s_addr, 0xffffffff); else param.netif = re->netif; } else { /* Try to get the interface from the key */ param.netif = ipnet_ioctl_netif_from_in_addr(sock, key.addr.s_addr, mask.addr.s_addr); } param.key = &key; if (IP_BIT_ISSET(param.flags, IPNET_RTF_HOST)) { if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_GATEWAY)) /* Must be link local host since no gateway was specified */ IP_BIT_SET(param.flags, IPNET_RTF_LLINFO); } else { param.netmask = &mask; if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_GATEWAY)) /* Must be link local net since no gateway was specified */ IP_BIT_SET(param.flags, IPNET_RTF_CLONING); } param.no_ref_count = IP_TRUE; return ipnet_route_add(¶m); } /* *=========================================================================== * ipnet_ioctl_x_route *=========================================================================== * Description: Adds or deletes route. Provides an API that is easily * mappable to the one used in Linux. * Parameters: request - IP_SIOCXADDRT or IP_SIOCXDELRT for add or delete a * IPv4/Ipv6 route. * Returns: 0 = success, <0 = error * */ IP_STATIC int ipnet_ioctl_x_route(Ipnet_socket *sock, unsigned long request, struct Ip_rtxmsg *rtmsg) { Ip_sa_family_t family = rtmsg->rt_dst.sa_family; struct Ipnet_route_add_param param; union Ipnet_route_key key; union Ipnet_route_key mask; ipcom_memset(¶m, 0, sizeof(param)); ipcom_memset(&key, 0, sizeof(key)); ipcom_memset(&mask, 0, sizeof(mask)); /* Determine address family */ switch (family) { #ifdef IPCOM_USE_INET case IP_AF_INET: { struct Ip_sockaddr_in *sin = (struct Ip_sockaddr_in*)&rtmsg->rt_dst; ipcom_memcpy(&key.ipv4.addr, &sin->sin_addr, sizeof(struct Ip_in_addr)); } break; #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_AF_INET6: { struct Ip_sockaddr_in6 *sin6 = (struct Ip_sockaddr_in6*)&rtmsg->rt_dst; ipcom_memcpy(&key.ipv6.addr, &sin6->sin6_addr, sizeof(struct Ip_in6_addr)); if (IPCOM_IN6_IS_ADDR_SCOPED(&key.ipv6.addr)) key.ipv6.scope_id = rtmsg->rt_ifindex; } break; #endif default: return -IP_ERRNO_EINVAL; } ipnet_route_create_mask(&mask, rtmsg->rt_dst_prefixlen); param.domain = family; param.vr = sock->vr_index; param.netif = ipnet_if_indextonetif(sock->vr_index, rtmsg->rt_ifindex);; param.key = &key; param.netmask = &mask; param.flags = rtmsg->rt_flags; param.no_ref_count = IP_TRUE; if (IP_BIT_ISSET(param.flags, IPNET_RTF_GATEWAY)) param.gateway = &rtmsg->rt_gateway; if (request == IP_SIOCXDELRT) return ipnet_route_delete2(param.domain, param.vr, IPCOM_ROUTE_TABLE_DEFAULT, param.key, param.netmask, param.gateway, 0, ipnet_get_effective_pid(), 0, IP_FALSE); if (IP_BIT_ISSET(param.flags, IPNET_RTF_HOST)) { if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_GATEWAY)) /* Must be link local host since no gateway was specified */ IP_BIT_SET(param.flags, IPNET_RTF_LLINFO); } else { if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_GATEWAY)) /* Must be link local net since no gateway was specified */ IP_BIT_SET(param.flags, IPNET_RTF_CLONING); } return ipnet_route_add(¶m); } /* *=========================================================================== * ipnet_ioctl_ip4 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipnet_ioctl_ip4(Ipnet_socket *sock, unsigned long request, struct Ip_ifreq *ifreq) { Ipnet_netif *netif; Ipnet_ip4_addr_entry *addr; struct Ip_sockaddr_in *in_addr; struct Ip_ifaliasreq *ifalias; Ip_u32 ipaddr_n; Ip_u32 netmask_n; Ip_u32 subbrd_n; int ret = 0; Ip_u16 flags = 0; struct Ip_sockaddr_dl dl; Ip_u32 netaddr_n; struct Ipnet_route_add_param param; if (request == IP_SIOCADDRT || request == IP_SIOCDELRT) return ipnet_ioctl_ip4_bsd43_compat(sock, request, (struct Ip_ortentry *) ifreq); ip_assert(IP_IOCPARM_LEN(request) >= IP_IFNAMSIZ); netif = ipnet_if_nametonetif(sock->vr_index, ifreq->ifr_name); if (netif == IP_NULL) return -IP_ERRNO_ENXIO; #ifdef IPPPP if (netif->ipcom.type == IP_IFT_PPP || netif->ipcom.type == IP_IFT_PPPOE) /* PPP special case. */ return ipnet_if_link_ioctl(netif, (Ip_u32)request, ifreq); #endif ifalias = (struct Ip_ifaliasreq *)ifreq; switch (request) { case IP_SIOCGIFADDR: case IP_SIOCSIFADDR: case IP_SIOCDIFADDR: case IP_SIOCGIFBRDADDR: case IP_SIOCSIFBRDADDR: case IP_SIOCGIFNETMASK: case IP_SIOCSIFNETMASK: case IP_SIOCGIFDSTADDR: case IP_SIOCSIFDSTADDR: in_addr = (struct Ip_sockaddr_in *) &ifreq->ifr_ifru.ifru_addr; break; #ifdef IP_SIOCAHOMEADDR case IP_SIOCAHOMEADDR: #endif case IP_SIOCAIFADDR: in_addr = (struct Ip_sockaddr_in *) &ifalias->ifra_addr; break; default: in_addr = IP_NULL; break; } if (in_addr != IP_NULL && IP_BIT_ISSET(IP_IOCF_W, request)) { #if defined(IPNET_COMPAT_OLDSOCK) && defined(IPCOM_USE_SA_LEN) if (in_addr->sin_family == 0 && in_addr->sin_len < IP_AF_MAX) ((struct Ip_sockaddr *)in_addr)->sa_family = in_addr->sin_len; ((struct Ip_sockaddr *)in_addr)->sa_len = sizeof(struct Ip_sockaddr_in); #endif if (in_addr->sin_family != IP_AF_INET || IPCOM_SA_LEN_GET(in_addr) < sizeof(struct Ip_sockaddr_in)) return -IP_ERRNO_EINVAL; } /* Get interface primary IPv4 address. */ if (request != IP_SIOCDIFADDR) { addr = netif->inet4_addr_list; if (addr != IP_NULL && addr->type != IPNET_ADDR_TYPE_UNICAST) addr = IP_NULL; } else { addr = ipnet_ip4_get_addr_entry(in_addr->sin_addr.s_addr, sock->vr_index, netif); if (addr == IP_NULL) return -IP_ERRNO_EADDRNOTAVAIL; } if (addr != IP_NULL) { ipaddr_n = addr->ipaddr_n; netmask_n = addr->netmask_n; subbrd_n = (addr->ipaddr_n & addr->netmask_n) | ~addr->netmask_n; } else { ipaddr_n = 0; netmask_n = 0; subbrd_n = 0; } switch (request) { case IP_SIOCGIFADDR: case IP_SIOCGIFBRDADDR: case IP_SIOCGIFNETMASK: case IP_SIOCGIFDSTADDR: 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)); break; #ifdef IP_SIOCAHOMEADDR case IP_SIOCAHOMEADDR: #endif case IP_SIOCAIFADDR: netmask_n = ((struct Ip_sockaddr_in *)&ifalias->ifra_mask)->sin_addr.s_addr; /* Fall though */ case IP_SIOCSIFADDR: ipaddr_n = in_addr->sin_addr.s_addr; break; default: break; } switch (request) { case IP_SIOCGIFADDR: if (!ipaddr_n) return -IP_ERRNO_EADDRNOTAVAIL; in_addr->sin_addr.s_addr = ipaddr_n; break; case IP_SIOCGIFBRDADDR: in_addr->sin_addr.s_addr = subbrd_n; break; case IP_SIOCGIFNETMASK: in_addr->sin_addr.s_addr = netmask_n; break; case IP_SIOCSIFNETMASK: if (addr == IP_NULL) return -IP_ERRNO_EINVAL; ret = ipnet_ip4_change_addr_mask(addr, in_addr->sin_addr.s_addr); break; case IP_SIOCSIFADDR: netmask_n = IP_NETMASK(in_addr->sin_addr.s_addr); /*Before remove the previous primary address,check the new primary address*/ if (ip_ntohl(netmask_n) < 0xfffffffe && ipaddr_n != IP_INADDR_ANY) { if ((in_addr->sin_addr.s_addr & ~netmask_n) == 0) return -IP_ERRNO_EINVAL; if ((in_addr->sin_addr.s_addr & ~netmask_n) == ~netmask_n) return -IP_ERRNO_EINVAL; } if (addr != IP_NULL) { /* Remove the previous primary address */ ret = ipnet_ip4_remove_addr(netif, addr->ipaddr_n); if (ret < 0) return ret; } #ifdef IP_PORT_LKM /* * Setting the address 0.0.0.0 is interpreted * as remove primary address on Linux. */ if (in_addr->sin_addr.s_addr == 0) break; #endif /*IP_PORT_LKM*/ flags |= IPNET_IP4_ADDR_FLAG_PRIMARY; /* Fall through */ case IP_SIOCAIFADDR: #ifdef IP_SIOCAHOMEADDR case IP_SIOCAHOMEADDR: if (request == IP_SIOCAHOMEADDR) flags |= IPNET_IP4_ADDR_FLAG_HOMEADDRESS | IPNET_IP4_ADDR_FLAG_PREFERRED; #endif ret = ipnet_ip4_add_addr2(netif, in_addr->sin_addr.s_addr, netmask_n, flags, IP_IN_CLASSD(in_addr->sin_addr.s_addr) ? IPNET_ADDR_TYPE_MULTICAST : IPNET_ADDR_TYPE_UNICAST); break; case IP_SIOCSIFBRDADDR: if (addr != IP_NULL && subbrd_n != in_addr->sin_addr.s_addr) { if (0 != (subbrd_n = ipnet_ip4_netif_get_subbrd(netif))) { /* Remove the old */ netaddr_n = subbrd_n; (void) ipnet_route_delete2(IP_AF_INET, netif->vr_index, IPCOM_ROUTE_TABLE_DEFAULT, &netaddr_n, IP_NULL, IP_NULL, netif->ipcom.ifindex, 0, 0, IP_FALSE); ipnet_ip4_remove_addr(netif,subbrd_n); } /* Add the new broadcast address */ ipnet_ip4_add_addr(netif, in_addr->sin_addr.s_addr, 0, IP_FALSE, IPNET_ADDR_TYPE_NETBROADCAST); netmask_n = 0xffffffff; /* Add the new network interface route */ ipcom_memset(&dl, 0, sizeof(struct Ip_sockaddr_dl)); IPCOM_SA_LEN_SET(&dl, sizeof(struct Ip_sockaddr_dl)); dl.sdl_family = IP_AF_LINK; dl.sdl_index = (Ip_u16)netif->ipcom.ifindex; dl.sdl_type = (Ip_u8)netif->ipcom.type; ipcom_memset(¶m, 0, sizeof(struct Ipnet_route_add_param)); param.domain = IP_AF_INET; param.vr = netif->vr_index; param.table = IPCOM_ROUTE_TABLE_DEFAULT; param.netif = netif; netaddr_n = in_addr->sin_addr.s_addr; param.gateway = (struct Ip_sockaddr *)&dl; param.key = &netaddr_n; param.netmask = &netmask_n; param.flags = IPNET_RTF_UP | IPNET_RTF_HOST | IPNET_RTF_DONE | IPNET_RTF_LLINFO | IPNET_RTF_PREF ; ret = ipnet_route_add(¶m); } else return -IP_ERRNO_EINVAL; break; case IP_SIOCDIFADDR: ret = ipnet_ip4_remove_addr(netif, in_addr->sin_addr.s_addr); break; default: return ipnet_if_link_ioctl(netif, (Ip_u32) request, ifreq); } return ret; } #endif /* IPCOM_USE_INET */ /* *=========================================================================== * ipnet_ioctl_ip6 *=========================================================================== * Description: * Parameters: * Returns: * */ #ifdef IPCOM_USE_INET6 IP_STATIC int ipnet_ioctl_ip6(Ipnet_socket *sock, unsigned long request, void *data) { Ipnet_netif *netif; struct Ip_in6_ifreq *ifreq = (struct Ip_in6_ifreq *) data; struct Ip_in6_aliasreq *ifareq = (struct Ip_in6_aliasreq *) data; Ip_u32 preferred_lifetime = IPCOM_ADDR_INFINITE; Ip_u32 valid_lifetime = IPCOM_ADDR_INFINITE; Ip_u32 flags = 0; ip_assert(IP_IOCPARM_LEN(request) >= IP_IFNAMSIZ); netif = ipnet_if_nametonetif(sock->vr_index, ifreq->ifr_name); if (netif == IP_NULL) return -IP_ERRNO_ENXIO; #ifdef IPPPP if (netif->ipcom.type == IP_IFT_PPP || netif->ipcom.type == IP_IFT_PPPOE) /* PPP special case. */ return ipnet_if_link_ioctl(netif, request, ifreq); #endif /* Sanity check */ switch (request) { case IP_SIOCAIFADDR_IN6: case IP_SIOCXGIFADDR_IN6: if (ifareq->ifra_addr.sin6_family != IP_AF_INET6 || IPCOM_SA_LEN_GET(&ifareq->ifra_addr) < sizeof(struct Ip_sockaddr_in6)) return -IP_ERRNO_EINVAL; break; case IP_SIOCDIFADDR_IN6: case IP_SIOCGIFPREFIX_IN6: case IP_SIOCGIFADDR_IN6: if (ifreq->ifr_ifru.ifru_addr.sin6_family != IP_AF_INET6 || IPCOM_SA_LEN_GET(&ifreq->ifr_ifru.ifru_addr) < sizeof(struct Ip_sockaddr_in6)) return -IP_ERRNO_EINVAL; break; case IP_SIOCGIFDSTADDR_IN6: if (!IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_POINTOPOINT)) return -IP_ERRNO_EINVAL; break; default: return -IP_ERRNO_EINVAL; } switch (request) { case IP_SIOCGIFADDR_IN6: { Ipnet_ip6_addr_entry *address = (Ipnet_ip6_addr_entry *)netif->inet6_addr_list; if(address == IP_NULL || address->type != IPNET_ADDR_TYPE_UNICAST) { return -IP_ERRNO_EADDRNOTAVAIL; } else { ipcom_memcpy(&ifreq->ifr_ifru.ifru_addr.sin6_addr, &address->addr, sizeof(struct Ip_in6_addr)); return 0; } break; } case IP_SIOCAIFADDR_IN6: { Ip_u32 ifindex = netif->ipcom.ifindex; int ret = 0; if (IP_IN6_IS_ADDR_UNSPECIFIED(&ifareq->ifra_addr.sin6_addr)) return -IP_ERRNO_EINVAL; if (IP_IN6_IS_ADDR_LOOPBACK(&ifareq->ifra_addr.sin6_addr) && (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_LOOPBACK))) return -IP_ERRNO_EINVAL; if (ifareq->ifra_lifetime.ia6t_preferred != (Ip_time_t)IPCOM_ADDR_INFINITE) preferred_lifetime = (Ip_u32) (ifareq->ifra_lifetime.ia6t_preferred - ipcom_time(IP_NULL)); if (ifareq->ifra_lifetime.ia6t_expire != (Ip_time_t)IPCOM_ADDR_INFINITE) valid_lifetime = (Ip_u32) (ifareq->ifra_lifetime.ia6t_expire - ipcom_time(IP_NULL)); if (IP_BIT_ISTRUE(ifareq->ifra_flags, IP_IN6_IFF_TEMPORARY)) flags |= IPNET_IP6_ADDR_FLAG_TEMPORARY; if (IP_BIT_ISTRUE(ifareq->ifra_flags, IP_IN6_IFF_TENTATIVE)) flags |= IPNET_IP6_ADDR_FLAG_TENTATIVE; if (IP_BIT_ISTRUE(ifareq->ifra_flags, IP_IN6_IFF_HOMEADDRESS)) flags |= IPNET_IP6_ADDR_FLAG_HOMEADDRESS; if (IP_BIT_ISTRUE(ifareq->ifra_flags, IP_IN6_IFF_AUTONOMOUS)) flags |= IPNET_IP6_ADDR_FLAG_AUTONOMOUS; if (IP_BIT_ISTRUE(ifareq->ifra_flags, IP_IN6_IFF_ANYCAST)) flags |= IPNET_IP6_ADDR_FLAG_ANYCAST; #ifdef IPNET_USE_RFC3971 if (IP_BIT_ISTRUE(ifareq->ifra_flags, IP_IN6_IFF_CGA)) flags |= IPNET_IP6_ADDR_FLAG_CGA; #endif ret = ipnet_ip6_add_addr(netif, &ifareq->ifra_addr.sin6_addr, flags, preferred_lifetime, valid_lifetime, ipcom_sockaddr_to_prefixlen((struct Ip_sockaddr *) &ifareq->ifra_prefixmask)); netif = ipnet_if_indextonetif(IPCOM_VR_ANY, ifindex); /* Check if peer address specified */ if (ret == 0 && netif != IP_NULL && ifareq->ifra_dstaddr.sin6_family == IP_AF_INET6 && IPCOM_SA_LEN_GET(&ifareq->ifra_dstaddr) == sizeof(struct Ip_sockaddr_in6)) { Ipnet_ppp_peer *peer = netif->private_data; /* Check if interface supports it */ if (peer != IP_NULL) ret = ipnet_if_set_ipv6_ppp_peer(peer, &ifareq->ifra_dstaddr.sin6_addr); else ret = -IP_ERRNO_EINVAL; if (ret != 0) (void)ipnet_ip6_remove_addr(netif, &ifareq->ifra_addr.sin6_addr); } return ret; } case IP_SIOCDIFADDR_IN6: return ipnet_ip6_remove_addr(netif, &ifreq->ifr_ifru.ifru_addr.sin6_addr); case IP_SIOCGIFDSTADDR_IN6: return ipnet_if_link_ioctl(netif, IP_SIOCGIFDSTADDR_IN6, &ifreq->ifr_ifru.ifru_addr.sin6_addr); case IP_SIOCXGIFADDR_IN6: { Ipnet_ip6_addr_entry *address = (Ipnet_ip6_addr_entry *)netif->inet6_addr_list; while (address != IP_NULL) { if (IP_IN6_ARE_ADDR_EQUAL(&address->addr, &ifareq->ifra_addr.sin6_addr)) { ifareq->ifra_flags = 0; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_TEMPORARY)) ifareq->ifra_flags |= IP_IN6_IFF_TEMPORARY; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_TENTATIVE)) ifareq->ifra_flags |= IP_IN6_IFF_TENTATIVE; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_HOMEADDRESS)) ifareq->ifra_flags |= IP_IN6_IFF_HOMEADDRESS; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_AUTONOMOUS)) ifareq->ifra_flags |= IP_IN6_IFF_AUTONOMOUS; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_ANYCAST)) ifareq->ifra_flags |= IP_IN6_IFF_ANYCAST; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_DEPRECATED)) ifareq->ifra_flags |= IP_IN6_IFF_DEPRECATED; if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_AUTOMATIC)) ifareq->ifra_flags |= IP_IN6_IFF_AUTOMATIC; #ifdef IPNET_USE_RFC3971 if (IP_BIT_ISSET(address->flags, IPNET_IP6_ADDR_FLAG_CGA)) ifareq->ifra_flags |= IP_IN6_IFF_CGA; #endif ifareq->ifra_lifetime.ia6t_expire = address->expires_sec; ifareq->ifra_lifetime.ia6t_preferred = address->preferred_sec; ifareq->ifra_lifetime.ia6t_pltime = 0; ifareq->ifra_lifetime.ia6t_vltime = 0; ipcom_memset(&ifareq->ifra_prefixmask, 0, sizeof(ifareq->ifra_prefixmask)); ipnet_route_create_mask(&ifareq->ifra_prefixmask.sin6_addr, address->prefixlen); return 0; } /* traverses the next address list */ address = (Ipnet_ip6_addr_entry *)address->next; } } break; case IP_SIOCGIFPREFIX_IN6: { Ipnet_ip6_addr_entry *address; address = (Ipnet_ip6_addr_entry *)netif->inet6_addr_list; while (address != IP_NULL) { if (IP_IN6_ARE_ADDR_EQUAL(&address->addr, &ifreq->ifr_ifru.ifru_addr.sin6_addr)) { ifreq->ifr_ifru.ifru_prefixlen = address->prefixlen; return (int) address->prefixlen; } /* traverses the next address list */ address = (Ipnet_ip6_addr_entry *)address->next; } } break; } return -IP_ERRNO_EINVAL; } #endif /* IPCOM_USE_INET6 */ /* *=========================================================================== * ipnet_socketioctl_may_flush_dst_cache *=========================================================================== * Description: Checks if this specific operation affect any destination * cache entries. * Parameters: vr - virtual router index * request - ioctl request * Returns: * */ IP_STATIC void ipnet_ioctl_may_flush_dst_cache(Ip_u16 vr, unsigned long request) { switch (request) { case IP_SIOCSARP: case IP_SIOCDARP: case IP_SIOCPARP: case IP_SIOCUARP: case IP_SIOCSIFLLADDR: case IP_SIOCXSDHCPRUNNING: case IP_SIOCSIFPRIVATE: case IP_SIOCXSECY: case IP_SIOCXSIFFEVENTCB: case IP_SIOCXDIFFEVENTCB: case IP_SIOCSROUTETABNAME: case IP_SIOCSETVLAN: case IP_SIOCSETVLANP: case IP_SIOCDELVLANP: case IP_SIOCSETVLANPVID: case IP_SIOCSETVLANPRIMAP: case IP_SIOCSETVLANINRULE: case IP_SIOCSETVLANOUTRULE: case IP_SIOCXRESETSTAT: case IP_SIOCXPCAP: case IP_SIOCSROHCSTATUS: case IP_SIOCSROHCCID: case IP_SIOCSETSTPID: case IP_SIOCADDMULTI: case IP_SIOCDELMULTI: /* * Cannot affect the destination cache */ break; case IP_SIOCSIFADDR: case IP_SIOCSIFBRDADDR: case IP_SIOCSIFNETMASK: case IP_SIOCSIFDSTADDR: case IP_SIOCAIFADDR: case IP_SIOCDIFADDR: case IP_SIOCADDRT: case IP_SIOCDELRT: case IP_SIOCXADDRT: case IP_SIOCXDELRT: case IP_SIOCAHOMEADDR: case IP_SIOCMIP4: /* * Can only affect AF_INET */ ipnet_dst_cache_flush(vr, IP_AF_INET); break; case IP_SIOCAIFADDR_IN6: case IP_SIOCDIFADDR_IN6: case IP_SIOCXSETGW_IN6: /* * Can only affect AF_INET6 */ ipnet_dst_cache_flush(vr, IP_AF_INET6); break; default: /* * Play it safe for things by default, operation may affect * existing entries in the destination cache... */ ipnet_dst_cache_flush(vr, 0); break; } } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_ioctl_move_if_rt_to_vr *=========================================================================== * Description: Moves all routes that are using the interface to the * new routing table. * Parameters: netif - The network interface that change route table. * vr - New virtual router index. * Returns: 0 = success, <0 = error code * */ IP_GLOBAL int ipnet_ioctl_move_if_rt_to_vr(Ipnet_netif *netif, Ip_u16 vr) { Ipnet_route_entry *rt; struct Ipnet_route_add_param rt_add_param; int n; Ip_size_t d; int domains[] = { #ifdef IPCOM_USE_INET IP_AF_INET, #endif #ifdef IPCOM_USE_INET6 IP_AF_INET6 #endif /* IPCOM_USE_INET */ }; if (vr == netif->vr_index) /* The interface is already using the specified route table */ return 0; if (ipnet_route_is_virtual_router_valid(vr) == IP_FALSE) { IPCOM_LOG2(WARNING, "%s cannot be assigned to virtual router %u since it does not exist", netif->ipcom.name, vr); return -IP_ERRNO_ENXIO; } #ifdef IP_PORT_LKM /* Don't allow multiple interfaces with identical names in the same VR */ if (ipnet_if_nametonetif(vr, netif->ipcom.name) != IP_NULL) return -IP_ERRNO_EPERM; #endif /* IP_PORT_LKM */ /* Issue the change VR event */ IPNET_NETLINKSOCK(ipnet_netlinksock_vrtab_change(netif->vr_index, netif->ipcom.name, vr)); /* Copy all routes that uses this interface to the new route table * Note: Two passes necessary because cloning routes need to be added first! */ for (d = 0; d < sizeof(domains) / sizeof(domains[0]); d++) { for (n = 0; n < 2; n++) { for (rt = ipnet_route_first_entry(domains[d], netif->vr_index, IPCOM_ROUTE_TABLE_DEFAULT); rt != IP_NULL; rt = ipnet_route_next_entry(rt)) { int ret; if (n == 0 && IP_BIT_ISFALSE(rt->hdr.flags, IPNET_RTF_CLONING)) continue; if (n == 1 && IP_BIT_ISTRUE(rt->hdr.flags, IPNET_RTF_CLONING)) continue; if (ipnet_ioctl_if_is_rt_affected(netif, rt) == IP_FALSE) continue; if (IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_X_MCAST_RO | IPNET_RTF_X_BCAST_RO)) { rt->netif = IP_NULL; IP_BIT_CLR(rt->hdr.flags, IPNET_RTF_UP); continue; } ipcom_memset(&rt_add_param, 0, sizeof(rt_add_param)); rt_add_param.domain = domains[d]; rt_add_param.flags = rt->hdr.flags; rt_add_param.gateway = rt->gateway; rt_add_param.key = rt->hdr.key; rt_add_param.metrics = &rt->metrics; if (IP_BIT_ISSET(rt->netif->ipcom.flags, IP_IFF_LOOPBACK)) rt_add_param.netif = ipnet_loopback_get_netif(vr); else rt_add_param.netif = rt->netif; rt_add_param.netmask = rt->hdr.mask; rt_add_param.pid = rt->pid; rt_add_param.seq = rt->seq; rt_add_param.vr = vr; rt_add_param.table = IPCOM_ROUTE_TABLE_DEFAULT; if (IP_BIT_ISSET(rt_add_param.flags, IPNET_RTF_MASK)) { int i; for (i = 0; i < rt->ref_count; i++) ret = ipnet_route_add(&rt_add_param); } else ret = ipnet_route_add(&rt_add_param); ip_assert2(ret == 0 || ret == -IP_ERRNO_EEXIST); (void)ret; } } } /* Remove all route that are using this interface from the old route table */ ipnet_ioctl_if_remove_routes(netif, netif->vr_index); #ifdef IPCOM_USE_INET { Ipnet_ip4_addr_entry *addr; for (addr = netif->inet4_addr_list; addr != IP_NULL; addr = addr->next) ipnet_ip4_remove_addr_cache(addr); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 { Ipnet_ip6_addr_entry *addr; for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next) ipnet_ip6_remove_addr_cache(addr); } #endif /* IPCOM_USE_INET */ IPNET_NETLINKSOCK(ipnet_rtnetlink_link_delete(netif)); #ifdef IPNET_USE_FAST_IFNAME_LOOKUP /* Remove from interface name hash table */ (void)ipcom_hash_remove(ipnet->ifname_tab,netif); #endif /* IPNET_USE_IFNAME_LOOKUP */ /* Change the route tab */ netif->vr_index = vr; #ifdef IPCOM_USE_INET { Ipnet_ip4_addr_entry *addr; for (addr = netif->inet4_addr_list; addr != IP_NULL; addr = addr->next) if (ipnet_ip4_insert_addr_cache(addr)) IP_PANIC(); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 { Ipnet_ip6_addr_entry *addr; for (addr = netif->inet6_addr_list; addr != IP_NULL; addr = addr->next) if (ipnet_ip6_insert_addr_cache(addr)) IP_PANIC(); } #endif /* IPCOM_USE_INET */ #ifdef IPNET_USE_FAST_IFNAME_LOOKUP /* Update interface name hash table with new VR index */ (void)ipcom_hash_add(ipnet->ifname_tab, netif); #endif /* IPNET_USE_FAST_IFNAME_LOOKUP */ IPNET_NETLINKSOCK(ipnet_rtnetlink_link_add(netif)); #ifdef IP_PORT_LKM ipcom_netif_assign_vr(&netif->ipcom, netif->vr_index); #endif /* IP_PORT_LKM */ return 0; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_sys_socketioctl *=========================================================================== * Description: IO control. * Parameters: fd - A socket descriptor. * request - The IO control request. * argp - Request specific data. * Returns: 0 = success, -1 = error (error code from ipcom_errno). * */ IP_PUBLIC int ipnet_sys_socketioctl(Ip_fd fd, unsigned long request, void *argp) { Ipnet_socket *sock; Ipnet_sock_ops *ops; int i; /* * This union is only used to avoid the gcc warning * "dereferencing type-punned pointer will break strict-aliasing rules" */ union { Ipcom_if_eventcb cb; void *ptr; } event_u; IPNET_FD_TO_OPEN_SOCKET(fd, sock); if (IP_BIT_ISSET(request, IP_IOCF_W)) { /* * Write operation, may change stack state. */ ipnet_sock_make_next_sendmsg_synchronous(sock); ipnet_ioctl_may_flush_dst_cache(sock->vr_index, request); } ops = ipnet_sock_get_ops(sock); if (ops->ioctl != IP_NULL) { i = ops->ioctl(sock, request, argp); if (i == 0 || i != -IP_ERRNO_EOPNOTSUPP) return i; /* else: try the IPNET ioctl handlers */ } switch (IP_IOC_GROUP(request)) { case IP_IOCG_SOCK: return ipnet_ioctl_sock(sock, request, argp); case IP_IOCG_RTAB: return ipnet_ioctl_rtab(request, argp); case IP_IOCG_POLICY_RT: return ipnet_route_policy_ioctl(ipnet_get_effective_pid(), sock->vr_index, (Ip_u32)request, (struct Ipnet_policy_routing_rule *) argp); case IP_IOCG_NETIF: case IP_IOCG_ETH: #ifdef IPPPP case IP_IOCG_PPP: #endif #ifdef IPNET_USE_WLAN case IP_IOCG_WLAN: #endif return ipnet_ioctl_if(sock, request, (struct Ip_ifreq *) argp); #ifdef IPCOM_USE_INET case IP_IOCG_INET: return ipnet_ioctl_ip4(sock, request, (struct Ip_ifreq *) argp); #ifdef IPNET_USE_MCAST_ROUTING case IP_IOCG_MCAST: return ipnet_ip4_mcast_ioctl(sock, request, argp); #endif #ifdef IPCOM_USE_ETHERNET case IP_IOCG_ARP: return ipnet_arp_ioctl(sock, request, (struct Ip_arpreq *) argp); #endif #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 case IP_IOCG_INET6: return ipnet_ioctl_ip6(sock, request, argp); #ifdef IPNET_USE_MCAST_ROUTING case IP_IOCG_MCAST_IN6: return ipnet_ip6_mcast_ioctl(sock, request, argp); #endif #endif /* IPCOM_USE_INET6 */ #ifdef IPMPLS case IP_IOCG_MPLS: return ipmpls_ioctl(request, argp); #endif #ifdef IPNET_USE_DIFFSERV case IP_IOCG_DS: return ipnet_diffserv_ioctl((Ip_u32) request, argp); #endif default: break; } switch (request) { #ifdef IPCOM_USE_INET6 case IP_SIOCXGETGW_IN6: /* Get default IPv6 gateway. */ return -IP_ERRNO_ENOSUPPORT; case IP_SIOCXSETGW_IN6: { /* Set default IPv6 gateway (including its optional scope id). */ struct Ipnet_route_add_param param; struct Ipnet_ipv6_key rt6; struct Ip_sockaddr_in6 gw6; Ipnet_route_entry *rt; ipcom_memset(¶m, 0, sizeof(struct Ipnet_route_add_param)); ipcom_memset(&rt6, 0, sizeof(struct Ipnet_ipv6_key)); ipcom_memset(&gw6, 0, sizeof(gw6)); param.domain = IP_AF_INET6; param.flags = IPNET_RTF_UP | IPNET_RTF_GATEWAY | IPNET_RTF_DONE | IPNET_RTF_STATIC; /* Key == :: */ param.key = &rt6; /* Netmask == /0 */ param.netmask = &rt6; /* Gateway */ param.gateway = (struct Ip_sockaddr *)&gw6; IPCOM_SA_LEN_SET(&gw6, sizeof(struct Ip_sockaddr_in6)); gw6.sin6_family = IP_AF_INET6; ipcom_memcpy(&gw6.sin6_addr, &((struct Ip_sockaddr_in6 *)argp)->sin6_addr, sizeof(struct Ip_in6_addr)); gw6.sin6_scope_id = ((struct Ip_sockaddr_in6 *)argp)->sin6_scope_id; if (ipnet_route_raw_lookup(IP_AF_INET6, sock->vr_index, IPCOM_ROUTE_TABLE_DEFAULT, IPNET_RTL_FLAG_DONTCLONE, &gw6.sin6_addr, gw6.sin6_scope_id, 0, &rt) < 0) return -IP_ERRNO_ENETUNREACH; ip_assert(rt); ip_assert(rt->netif); param.netif = rt->netif; param.vr = sock->vr_index; param.table = IPCOM_ROUTE_TABLE_DEFAULT; return ipnet_route_add(¶m); } #endif /* IPCOM_USE_INET6 */ case IP_SIOCXSIFFEVENTCB: /* Set interface event callback. */ for (i = 0; i < IPNET_MAX_IF_EVENTCB; i++) if (ipnet->if_eventcb[i] == IP_NULL) { event_u.ptr = argp; ipnet->if_eventcb[i] = event_u.cb; return 0; } return -IP_ERRNO_EINVAL; case IP_SIOCXDIFFEVENTCB: /* Delete interface event callback. */ event_u.ptr = argp; for (i = 0; i < IPNET_MAX_IF_EVENTCB; i++) if (ipnet->if_eventcb[i] == event_u.cb) { ipnet->if_eventcb[i] = IP_NULL; return 0; } return -IP_ERRNO_ENOENT; #ifdef IPIPSEC2 case IP_SIOCXIPSEC_SELECTOR: case IP_SIOCXIPSEC_NEXT_SA: case IP_SIOCXIPSEC_Grouped: case IP_SIOCXIPSEC_InUserOctets: case IP_SIOCXIPSEC_OutUserOctets: case IP_SIOCXIPSEC_InUserPackets: case IP_SIOCXIPSEC_OutUserPackets: return ipipsec_sys_ioctl(fd, request, argp); #ifdef IPIPSEC_USE_CTRL_API case IP_SIOCXIPSEC_CTL: return ipipsec_ctrl_internal(((Ip_tag **)argp)[0]); case IP_SIOCXIPSEC_SA_CTL: return ipipsec_sactrl_internal(((void **)argp)[0], ((void **)argp)[1]); case IP_SIOCXIPSEC_CONF_CTL: return ipipsec_conf_internal(((void **)argp)[0]); #endif #endif /* IPIPSEC2 */ #ifdef IPCOM_USE_INET case IP_SIOCXADDRT: case IP_SIOCXDELRT: return ipnet_ioctl_x_route(sock, request, argp); #endif /* IPCOM_USE_INET */ #ifdef IPROHC case IP_SIOCSROHCSTATUS: case IP_SIOCROHCADDIF: case IP_SIOCROHCDELIF: case IP_SIOCROHCADDIFPROFILE: case IP_SIOCROHCDELIFPROFILE: case IP_SIOCSROHCCID: return iprohc_ioctl(request, (struct Ip_rohcreq *) argp); #endif /* IPROHC */ #ifdef IPNET_USE_VLAN_STACKING case IP_SIOCSETSTPID: case IP_SIOCGETSTPID: return ipnet_vlan_stacking_ioctl(request, argp, IP_NULL); #endif /*IPNET_USE_VLAN_STACKING */ default: break; } return -IP_ERRNO_EINVAL; } /* **************************************************************************** * END OF FILE **************************************************************************** */