| /* |
| * module_tunnel.c: Tunnel module |
| * |
| * Copyright (C) 2007 Mindspeed Technologies, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #include <sys/ioctl.h> |
| #include <linux/sockios.h> |
| #include <net/if.h> |
| #include <net/if_arp.h> |
| |
| #include "cmm.h" |
| #include "fpp.h" |
| #include "cmmd.h" |
| #include "module_tunnel.h" |
| #include <linux/if_tun.h> |
| |
| #ifdef SAM_LEGACY |
| extern int rt_mw_sam_make_dst_ipv6( struct in_addr *sam_dst_ipv4_addr, ushort sam_port, struct in6_addr *sam_dst_ipv6_addr ); |
| extern int rt_mw_sam_get_ipv6( struct in6_addr *sam_ipv6_addr ); |
| extern int rt_mw_sam_get_portsetid( rt_mw_ipstack_sam_port_t *sam_psid ); |
| unsigned short TunMtu = DEFAULT_SAM_FRAG_MTU; |
| #endif |
| |
| extern void __itf_update_connection(FCI_CLIENT *fci_handle, int ifindex); |
| |
| /************************************************************ |
| * |
| * tunnel_print_usage |
| * Role : Get tunnel info from kernel |
| ************************************************************/ |
| static int tunnel_print_usage() |
| { |
| cmm_print(DEBUG_ERROR, |
| "Usage: tunnel <name> add [ethipoip6] [ipsec {0|1}]\n" |
| " tunnel <name> del\n" |
| " tunnel <name> show \n" |
| "\n" |
| "\n" |
| " Ex: set tunnel tnl0 add ethipoip6 ipsec 1\n" |
| " del tunnel tnl0\n" |
| ); |
| #ifdef SAM_LEGACY |
| cmm_print(DEBUG_ERROR, |
| " tunnel <name> set <sam_enable/sam_disable> [sam-frag-mtu <mtu>] \n"); |
| #endif |
| return 0; |
| } |
| |
| /************************************************************ |
| * |
| * tunnel_print_info |
| * |
| ************************************************************/ |
| static int tunnel_print_info(struct interface *itf) |
| { |
| if (itf->tunnel_family == AF_INET6) |
| { |
| char remote[INET6_ADDRSTRLEN]; |
| char local[INET6_ADDRSTRLEN]; |
| char ifname[IFNAMSIZ]; |
| |
| inet_ntop(AF_INET6, itf->tunnel_parm6.raddr.s6_addr, remote, INET6_ADDRSTRLEN); |
| inet_ntop(AF_INET6, itf->tunnel_parm6.laddr.s6_addr, local, INET6_ADDRSTRLEN); |
| |
| cmm_print(DEBUG_STDOUT, "Tunnel name : %s\n", if_indextoname(itf->ifindex, ifname)); |
| if( itf->tunnel_parm6.proto == IPPROTO_IPIP) |
| cmm_print(DEBUG_STDOUT, "Protocol : ip 4 over 6 (%d)\n", itf->tunnel_parm6.proto); |
| else |
| cmm_print(DEBUG_STDOUT, "Protocol : etherip over ip6 (%d)\n", itf->tunnel_parm6.proto); |
| cmm_print(DEBUG_STDOUT, "Local address : %s\n", local); |
| cmm_print(DEBUG_STDOUT, "Remote address : %s\n", remote); |
| cmm_print(DEBUG_STDOUT, "Output device : %s\n", if_indextoname(itf->phys_ifindex, ifname)); |
| cmm_print(DEBUG_STDOUT, "Flags : %x\n", itf->tunnel_flags); |
| |
| if(itf->tunnel_flags & TNL_IPSEC) |
| cmm_print(DEBUG_STDOUT, "Secure : yes\n"); |
| else |
| cmm_print(DEBUG_STDOUT, "Secure : no\n"); |
| |
| if (itf->tunnel_parm6.flags & IP6_TNL_F_IGN_ENCAP_LIMIT) |
| cmm_print(DEBUG_STDOUT, "Encap limit : none\n"); |
| else |
| cmm_print(DEBUG_STDOUT, "Encap limit : %d\n", itf->tunnel_parm6.encap_limit); |
| |
| if (itf->flags & FPP_PROGRAMMED) |
| cmm_print(DEBUG_STDOUT, "Status : running\n"); |
| else |
| { |
| cmm_print(DEBUG_STDOUT, "Status : not complete\n"); |
| |
| if (!itf->rt.route) |
| cmm_print(DEBUG_STDOUT, " -> Waiting for neigh info\n"); |
| |
| if (!itf->flow_rep || ! itf->flow_orig) |
| cmm_print(DEBUG_STDOUT, " -> Waiting for ipsec info\n"); |
| } |
| } |
| else if (itf->tunnel_family == AF_INET) |
| { |
| char remote[INET_ADDRSTRLEN]; |
| char local[INET_ADDRSTRLEN]; |
| char ifname[IFNAMSIZ]; |
| char prefix[INET6_ADDRSTRLEN]; |
| char relayprefix[INET_ADDRSTRLEN]; |
| |
| inet_ntop(AF_INET, &itf->tunnel_parm4.iph.daddr, remote, INET_ADDRSTRLEN); |
| inet_ntop(AF_INET, &itf->tunnel_parm4.iph.saddr, local, INET_ADDRSTRLEN); |
| |
| cmm_print(DEBUG_STDOUT, "Tunnel name : %s\n", if_indextoname(itf->ifindex, ifname)); |
| cmm_print(DEBUG_STDOUT, "Protocol : 6o4 (%d)\n", itf->tunnel_parm4.iph.protocol); |
| cmm_print(DEBUG_STDOUT, "Local address : %s\n", local); |
| cmm_print(DEBUG_STDOUT, "Remote address : %s\n", remote); |
| cmm_print(DEBUG_STDOUT, "Output device : %s\n", if_indextoname(itf->phys_ifindex, ifname)); |
| cmm_print(DEBUG_STDOUT, "Flags : %x\n", itf->tunnel_flags); |
| |
| if (itf->flags & FPP_PROGRAMMED) |
| cmm_print(DEBUG_STDOUT, "Status : running\n"); |
| else |
| { |
| cmm_print(DEBUG_STDOUT, "Status : not complete\n"); |
| |
| if (!itf->rt.route) |
| cmm_print(DEBUG_STDOUT, " -> Waiting for neigh info\n"); |
| |
| } |
| |
| if (itf->tunnel_flags & TNL_6RD) |
| { |
| cmm_print(DEBUG_STDOUT, "6rd-prefix : %s\n", inet_ntop(AF_INET6, &itf->tunnel_parm6rd.prefix, prefix, INET6_ADDRSTRLEN)); |
| cmm_print(DEBUG_STDOUT, "6rd-prefixlen : %d\n", itf->tunnel_parm6rd.prefixlen); |
| cmm_print(DEBUG_STDOUT, "6rd-relayprefix : %s\n", inet_ntop(AF_INET, &itf->tunnel_parm6rd.relay_prefix, relayprefix, INET_ADDRSTRLEN)); |
| cmm_print(DEBUG_STDOUT, "6rd-relayprefixlen : %d\n", itf->tunnel_parm6rd.relay_prefixlen); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /************************************************************ |
| * |
| * tunnel_parse_cmd |
| * |
| ************************************************************/ |
| static int tunnel_parse_cmd(int argc, char ** keywords, daemon_handle_t daemon_handle) |
| { |
| char *tnl_name; |
| char tnl_type; |
| char ipsec = 0; |
| cmmd_tunnel_t cmmtd_cmd; /* CMM to Deamon command */ |
| char rcvBuffer[1024]; |
| int rc; |
| |
| if (argc < 2) |
| return tunnel_print_usage(); |
| |
| memset(&cmmtd_cmd,0,sizeof cmmtd_cmd); |
| tnl_name = *keywords++; |
| |
| if (strncmp(*keywords, "add", strlen(*keywords)) == 0) |
| { |
| keywords++; |
| |
| if((argc != 3) && (argc != 5)) |
| return tunnel_print_usage(); |
| |
| if (strcmp(*keywords, "ethipoip6") == 0) |
| tnl_type = TNL_ETHIPOIP6; |
| else |
| { |
| return tunnel_print_usage(); |
| } |
| |
| if (argc == 5) |
| { |
| keywords++; |
| |
| if ((strncmp(*keywords, "ipsec", strlen(*keywords)) == 0)) |
| { |
| keywords++; |
| |
| if ((strncmp(*keywords, "0", strlen(*keywords)) == 0) |
| || (strncmp(*keywords, "1", strlen(*keywords)) == 0)) |
| { |
| ipsec = atoi(*keywords); |
| } |
| else |
| { |
| return tunnel_print_usage(); |
| } |
| } |
| else |
| { |
| return tunnel_print_usage(); |
| } |
| } |
| strcpy(cmmtd_cmd.name, tnl_name); |
| cmmtd_cmd.ipsec = ipsec; |
| cmmtd_cmd.tunnel_type = tnl_type; |
| |
| /* Send CMD_CMMTD_TUNNEL_ADD to Deamon !*/ |
| rc = cmmSendToDaemon(daemon_handle, CMMD_CMD_TUNNEL_ADD, &cmmtd_cmd, sizeof(cmmtd_cmd), &rcvBuffer); |
| if (rc != 2) /* we expect 2 bytes in response */ |
| { |
| if (rc >= 0) |
| cmm_print(DEBUG_STDERR, "CMD_TUNNEL_ADD unexpected response length %d\n", rc); |
| return -1; |
| } |
| else if ((((u_int16_t*)rcvBuffer)[0]) != CMMD_ERR_OK) |
| { |
| cmm_print(DEBUG_STDERR, "Error %d received from CMM Deamon for CMD_TUNNEL_ADD\n", ((u_int16_t*)rcvBuffer)[0]); |
| return -1; |
| } |
| |
| return 0; |
| } |
| else if(strncmp(*keywords, "show", strlen(*keywords)) == 0) |
| { |
| if(argc != 2) |
| return tunnel_print_usage(); |
| else |
| { |
| struct interface *itf; |
| |
| cmm_print(DEBUG_STDOUT, "Details for tunnel %s\n", tnl_name); |
| strcpy(cmmtd_cmd.name, tnl_name); |
| |
| /* Send CMD_CMMTD_TUNNEL_SHOW to Deamon !*/ |
| rc = cmmSendToDaemon(daemon_handle, CMMD_CMD_TUNNEL_SHOW, &cmmtd_cmd, sizeof(cmmtd_cmd), rcvBuffer); |
| if (rc != (sizeof(struct interface) + 4)) |
| { |
| if(rc >= 0) |
| cmm_print(DEBUG_STDERR, "ERROR: CMD_TUNNEL_SHOW Unexpected result returned from FPP rc:%04x - received %d - expected %d\n", |
| (rc < sizeof(unsigned short) ) ? 0 : *((unsigned short *) rcvBuffer), |
| rc, |
| sizeof(struct interface) + 4 |
| ); |
| return -1; |
| } |
| else if ((((unsigned short*)rcvBuffer)[0]) != CMMD_ERR_OK) |
| { |
| cmm_print(DEBUG_STDERR, "Error %d received from CMM Deamon for CMD_TUNNEL_DEL\n", ((unsigned short*)rcvBuffer)[0]); |
| return -1; |
| } |
| |
| itf = (struct interface *)(rcvBuffer + 4); |
| tunnel_print_info(itf); |
| } |
| } |
| else if(strncmp(*keywords, "del", strlen(*keywords)) == 0) |
| { |
| if(argc != 2) |
| return tunnel_print_usage(); |
| else |
| { |
| strcpy(cmmtd_cmd.name, tnl_name); |
| |
| /* Send CMD_CMMTD_TUNNEL_DEL to Deamon !*/ |
| rc = cmmSendToDaemon(daemon_handle, CMMD_CMD_TUNNEL_DEL, &cmmtd_cmd, sizeof(cmmtd_cmd), &rcvBuffer); |
| if (rc != 2) |
| { |
| if (rc >= 0) |
| cmm_print(DEBUG_STDERR, "CMD_TUNNEL_DEL unexpected response length %d\n", rc); |
| return -1; |
| } |
| else if ((((unsigned short*)rcvBuffer)[0]) != CMMD_ERR_OK) |
| { |
| cmm_print(DEBUG_STDERR, "Error %d received from CMM Deamon for CMD_TUNNEL_DEL\n", ((unsigned short*)rcvBuffer)[0]); |
| return -1; |
| } |
| } |
| } |
| #ifdef SAM_LEGACY |
| else if (strncmp(*keywords, "set", strlen(*keywords)) ==0) |
| { |
| unsigned int tmp; |
| char * endptr = NULL; |
| keywords++; |
| if((argc < 3) ||((argc >3) && (argc <5))) |
| return tunnel_print_usage(); |
| strcpy(cmmtd_cmd.name, tnl_name); |
| cmmtd_cmd.tunnel_type = TNL_4O6; |
| if(strncasecmp(*keywords,"sam_enable", strlen(*keywords)) == 0) |
| cmmtd_cmd.sam_enable = 1; |
| else if(strncasecmp(*keywords,"sam_disable", strlen(*keywords)) == 0) |
| cmmtd_cmd.sam_enable = 0; |
| else |
| return tunnel_print_usage(); |
| cmmtd_cmd.tun_mtu = DEFAULT_SAM_FRAG_MTU; |
| if( argc >3 ) |
| { |
| keywords++; |
| if(strncasecmp(*keywords,"sam-frag-mtu",strlen(*keywords))==0) |
| { |
| keywords++; |
| tmp = strtoul(*keywords, &endptr, 0); |
| cmmtd_cmd.tun_mtu= tmp; |
| } |
| } |
| |
| if(cmmSendToDaemon(daemon_handle, CMMD_CMD_TUNNEL_SAMREADY, &cmmtd_cmd, sizeof(cmmtd_cmd), &rcvBuffer) == 1) |
| { |
| if ((((unsigned short*)rcvBuffer)[0]) != 0) |
| { |
| cmm_print(DEBUG_STDERR, "error %d received from cmm deamon for cmmd_cmd_cmmtd_tunnel_samready\n", ((unsigned short*)rcvBuffer)[0]); |
| return -1; |
| } |
| } |
| |
| } |
| #endif |
| else |
| return tunnel_print_usage(); |
| |
| return 0; |
| } |
| |
| /************************************************************ |
| * |
| * cmm_tunnel_parse_cmd |
| * |
| ************************************************************/ |
| int cmm_tunnel_parse_cmd(int argc, char ** keywords, int tabStart, daemon_handle_t daemon_handle) |
| { |
| if (tabStart < argc) |
| return tunnel_parse_cmd(argc - tabStart, &keywords[tabStart], daemon_handle); |
| else |
| return tunnel_print_usage(); |
| } |
| |
| /************************************************************ |
| * |
| * tunnel_send_cmd |
| * Role: CMM to FPP commands in deamon context |
| ************************************************************/ |
| int tunnel_send_cmd(FCI_CLIENT *fci_handle, int request, struct interface *itf) |
| { |
| cmmd_tunnel_create_cmd_t cmd; |
| int action; |
| int ret = CMMD_ERR_OK; |
| |
| switch (request) |
| { |
| case (ADD | UPDATE): |
| if ((itf->flags & (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) == FPP_PROGRAMMED) |
| goto out; |
| |
| if ((itf->flags & (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) == (FPP_PROGRAMMED | FPP_NEEDS_UPDATE)) |
| action = FPP_ACTION_UPDATE; |
| else |
| action = FPP_ACTION_REGISTER; |
| |
| break; |
| |
| case UPDATE: |
| if (!((itf->flags & FPP_PROGRAMMED) && (itf->flags & FPP_NEEDS_UPDATE))) |
| goto out; |
| |
| action = FPP_ACTION_UPDATE; |
| break; |
| |
| default: |
| cmm_print(DEBUG_ERROR, "%s: Command not supported\n", __func__); |
| ret = CMMD_ERR_UNKNOWN_COMMAND; |
| goto out; |
| break; |
| } |
| |
| memset(&cmd, 0, sizeof(cmd)); |
| |
| if (itf->tunnel_family == AF_INET6) |
| { |
| if (itf->tunnel_parm6.proto == IPPROTO_ETHERIP) |
| { |
| cmd.mode = TNL_ETHIPOIP6; |
| } |
| else if (itf->tunnel_parm6.proto == IPPROTO_IPIP) |
| { |
| cmd.mode = TNL_4O6; |
| } |
| |
| memcpy(cmd.local, itf->tunnel_parm6.laddr.s6_addr, 16); |
| if(!(itf->tunnel_flags & TNL_4RD)) |
| { |
| memcpy(cmd.remote, itf->tunnel_parm6.raddr.s6_addr, 16); |
| } |
| |
| if (itf->tunnel_parm6.flags & IP6_TNL_F_IGN_ENCAP_LIMIT) |
| cmd.encap_limit = 0; |
| else |
| cmd.encap_limit = itf->tunnel_parm6.encap_limit; |
| |
| cmd.hop_limit = itf->tunnel_parm6.hop_limit; |
| |
| /* Flowinfo : flowclass / traffic class will need to be detailed */ |
| cmd.flow_info = itf->tunnel_parm6.flowinfo; |
| |
| if (itf->tunnel_flags & TNL_IPSEC) |
| cmd.secure = 1; |
| else |
| cmd.secure = 0; |
| |
| } |
| else |
| { |
| if (itf->type == ARPHRD_SIT) |
| cmd.mode = TNL_6O4; |
| |
| memcpy(cmd.local, &itf->tunnel_parm4.iph.saddr, 4); |
| memcpy(cmd.remote, &itf->tunnel_parm4.iph.daddr, 4); |
| |
| cmd.hop_limit = itf->tunnel_parm4.iph.ttl; |
| cmd.flow_info = itf->tunnel_parm4.iph.tos; |
| cmd.frag_off = itf->tunnel_parm4.iph.frag_off; |
| |
| /* For now not supported */ |
| cmd.secure = 0; |
| } |
| |
| cmd.route_id = itf->rt.fpp_route_id; |
| |
| cmd.enabled = itf->tunnel_enabled; |
| |
| if (____itf_get_name(itf, cmd.name, sizeof(cmd.name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| ret = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto out; |
| } |
| |
| #if 0 |
| if (__itf_get_name(itf->phys_ifindex, cmd.output_device, sizeof(cmd.output_device)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: __itf_get_name(%d) failed\n", __func__, itf->phys_ifindex); |
| goto err; |
| } |
| #endif |
| |
| if (action == FPP_ACTION_REGISTER) |
| { |
| //Send message to forward engine |
| cmm_print(DEBUG_COMMAND, "Send CMD_TUNNEL_ADD\n"); |
| |
| ret = fci_write(fci_handle, FPP_CMD_TUNNEL_ADD, sizeof(fpp_tunnel_create_cmd_t), (unsigned short *)&cmd); |
| if ((ret == FPP_ERR_OK) || (ret == FPP_ERR_TNL_ALREADY_CREATED)) |
| { |
| itf->flags |= FPP_PROGRAMMED; |
| itf->flags &= ~FPP_NEEDS_UPDATE; |
| } |
| else |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_TUNNEL_ADD\n", __func__, ret); |
| goto out; |
| } |
| } |
| else |
| { |
| //Send message to forward engine |
| cmm_print(DEBUG_COMMAND, "Send CMD_TUNNEL_UPDATE\n"); |
| |
| ret = fci_write(fci_handle, FPP_CMD_TUNNEL_UPDATE, sizeof(fpp_tunnel_create_cmd_t), (unsigned short *)&cmd); |
| if (ret == FPP_ERR_OK) |
| { |
| itf->flags &= ~FPP_NEEDS_UPDATE; |
| } |
| else |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_TUNNEL_UPDATE\n", __func__, ret); |
| goto out; |
| } |
| } |
| |
| out: |
| return ret; |
| } |
| |
| |
| static int tunnel_send_del(FCI_CLIENT *fci_handle, struct interface *itf) |
| { |
| fpp_tunnel_del_cmd_t cmd; |
| int ret = 0; |
| |
| if (!(itf->flags & FPP_PROGRAMMED)) |
| return CMMD_ERR_OK; |
| |
| memset(&cmd, 0, sizeof(cmd)); |
| |
| if (____itf_get_name(itf, cmd.name, sizeof(cmd.name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| return CMMD_ERR_WRONG_COMMAND_PARAM; |
| } |
| |
| //Send message to forward engine |
| cmm_print(DEBUG_COMMAND, "Send CMD_TUNNEL_DEL\n"); |
| |
| ret = fci_write(fci_handle, FPP_CMD_TUNNEL_DEL, sizeof(fpp_tunnel_del_cmd_t), (unsigned short *) &cmd); |
| if (ret == FPP_ERR_TNL_ENTRY_NOT_FOUND || ret == FPP_ERR_OK) |
| itf->flags &= ~FPP_PROGRAMMED; |
| else |
| { |
| if(ret > 0) |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_TUNNEL_DEL\n", __func__, ret); |
| else |
| cmm_print(DEBUG_ERROR, "%s: Error '%s' while sending CMD_TUNNEL_DEL\n", __func__, strerror(errno)); |
| } |
| |
| return ret; |
| } |
| /************************************************************ |
| * |
| * tunnel_update_sa |
| * Role : Update FPP tunnel SA and local cmm copy |
| ************************************************************/ |
| static int tunnel_update_sa(FCI_CLIENT *fci_handle, struct interface *itf, unsigned char orig) |
| { |
| fpp_tunnel_sec_cmd_t cmd; |
| int ret; |
| |
| if (!(itf->flags & FPP_PROGRAMMED)) |
| goto out; |
| |
| memset(&cmd, 0, sizeof(cmd)); |
| |
| if (____itf_get_name(itf, cmd.name, sizeof(cmd.name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| goto err; |
| } |
| |
| if (orig) |
| { |
| cmd.sa_nr = itf->flow_orig->sa_nr; |
| memcpy(cmd.sa_handle, itf->flow_orig->sa_handle, itf->flow_orig->sa_nr * sizeof(unsigned short)); |
| } |
| else |
| { |
| cmd.sa_reply_nr = itf->flow_rep->sa_nr; |
| memcpy(cmd.sa_reply_handle, itf->flow_rep->sa_handle, itf->flow_rep->sa_nr * sizeof(unsigned short)); |
| } |
| |
| //Send message to forward engine |
| cmm_print(DEBUG_COMMAND, "Send CMD_TUNNEL_SEC\n"); |
| |
| if ((ret = fci_write(fci_handle, FPP_CMD_TUNNEL_SEC, sizeof(fpp_tunnel_sec_cmd_t), (unsigned short *) &cmd))) |
| { |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending CMD_TUNNEL_SEC\n", __func__, ret); |
| goto err; |
| } |
| |
| out: |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| |
| /************************************************************ |
| * |
| * __tunnel_add |
| * |
| ************************************************************/ |
| int __tunnel_add(FCI_CLIENT *fci_handle, struct interface *itf) |
| { |
| unsigned int *sAddr, *dAddr; |
| unsigned char proto; |
| int enabled = itf->tunnel_enabled; |
| int rc = CMMD_ERR_NOT_CONFIGURED; |
| |
| if (!__itf_is_up(itf)) |
| goto err; |
| |
| if (!(itf->flags & USER_ADDED)) |
| goto err; |
| |
| if (itf->tunnel_family == AF_INET6) |
| { |
| dAddr = itf->tunnel_parm6.raddr.s6_addr32; |
| proto = itf->tunnel_parm6.proto; |
| #ifdef SAM_LEGACY |
| if(proto == IPPROTO_IPIP) |
| { |
| if ((!itf->sam_enable) && (!rt_mw_sam_get_ipv6(&itf->tunnel_parm6.laddr)) ) |
| { |
| cmm_print(DEBUG_INFO,"Tunnel %s is up but SAM is not yet Ready", itf->ifname ); |
| goto err; |
| } |
| } |
| #endif |
| |
| sAddr = itf->tunnel_parm6.laddr.s6_addr32; |
| } |
| else |
| { |
| sAddr = &itf->tunnel_parm4.iph.saddr; |
| dAddr = &itf->tunnel_parm4.iph.daddr; |
| proto = itf->tunnel_parm4.iph.protocol; |
| } |
| |
| if (((itf->type != ARPHRD_SIT) && (itf->tunnel_parm6.proto != IPPROTO_IPIP)) || dAddr[0]) |
| { |
| struct flow flow; |
| |
| if (itf->tunnel_flags & TNL_IPSEC) |
| { |
| if (!itf->flow_orig) |
| { |
| itf->flow_orig = __cmmFlowGet(itf->tunnel_family, sAddr, dAddr, 0, 0, proto); |
| } |
| |
| if (!itf->flow_rep) |
| { |
| itf->flow_rep = __cmmFlowGet(itf->tunnel_family, dAddr, sAddr, 0, 0, proto); |
| } |
| } |
| |
| flow.family = itf->tunnel_family; |
| flow.sAddr = sAddr; |
| flow.dAddr = dAddr; |
| flow.fwmark = 0; |
| flow.iifindex = 0; |
| |
| rc = __cmmRouteRegister(&itf->rt, &flow, 0, "tunnel"); |
| |
| if (itf->rt.route) |
| itf->phys_ifindex = itf->rt.route->oifindex; |
| |
| if (rc < 0) |
| { |
| enabled = 0; |
| goto program; |
| } |
| |
| enabled = 1; |
| |
| cmmFeRouteUpdate(fci_handle, ADD | UPDATE, itf->rt.fpp_route); |
| } |
| else |
| enabled = 1; |
| |
| program: |
| if (itf->tunnel_enabled != enabled) |
| { |
| itf->flags |= FPP_NEEDS_UPDATE; |
| itf->tunnel_enabled = enabled; |
| } |
| |
| __cmmCheckFPPRouteIdUpdate(&itf->rt, &itf->flags); |
| |
| rc = tunnel_send_cmd(fci_handle, ADD | UPDATE, itf); |
| |
| if (rc != CMMD_ERR_OK) |
| goto err; |
| |
| if (itf->tunnel_flags & TNL_IPSEC) |
| { |
| if (itf->flow_orig) |
| tunnel_update_sa(fci_handle, itf, 1); |
| |
| if (itf->flow_rep) |
| tunnel_update_sa(fci_handle, itf, 0); |
| } |
| |
| err: |
| return rc; |
| } |
| |
| |
| /************************************************************ |
| * |
| * tunnel_add |
| * |
| ************************************************************/ |
| static int tunnel_add(FCI_CLIENT *fci_handle, char *name, unsigned char ipsec, char tnl_type, u_int16_t *res_buf, u_int16_t *res_len) |
| { |
| int ifindex; |
| struct interface *itf; |
| int rc = 0; |
| int update_connections = 0; |
| |
| __pthread_mutex_lock(&itf_table.lock); |
| __pthread_mutex_lock(&ctMutex); |
| __pthread_mutex_lock(&rtMutex); |
| __pthread_mutex_lock(&neighMutex); |
| __pthread_mutex_lock(&flowMutex); |
| |
| ifindex = if_nametoindex(name); |
| |
| itf = __itf_get(ifindex); |
| if (!itf) |
| { |
| cmm_print(DEBUG_ERROR, "%s: interface %s not found\n", __func__, name); |
| res_buf[0] = CMMD_ERR_NOT_FOUND; |
| goto err0; |
| } |
| |
| if (!__itf_is_tunnel(itf)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: interface %s is not a tunnel\n", __func__, name); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| |
| switch (tnl_type) |
| { |
| case TNL_ETHIPOIP6: |
| if (itf->tunnel_family != AF_INET6) |
| { |
| cmm_print(DEBUG_ERROR, "%s: tunnel type %x can't have family %d\n", __func__, tnl_type, itf->tunnel_family); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| |
| if (itf->tunnel_parm6.proto != IPPROTO_ETHERIP) |
| { |
| cmm_print(DEBUG_ERROR, "%s: tunnel type %x can't have proto %d\n", __func__, tnl_type, itf->tunnel_parm6.proto); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| |
| if (ipsec) |
| itf->tunnel_flags |= TNL_IPSEC; |
| else |
| itf->tunnel_flags &= ~TNL_IPSEC; |
| |
| |
| break; |
| |
| #ifdef SAM_LEGACY |
| case TNL_4O6: |
| if (itf->tunnel_family != AF_INET6) |
| { |
| cmm_print(DEBUG_ERROR, "%s: tunnel type %x can't have family %d\n", __func__, tnl_type, itf->tunnel_family); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| if ((itf->tunnel_parm6.proto != IPPROTO_ETHERIP) && (itf->tunnel_parm6.proto != IPPROTO_IPIP)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: tunnel type %x can't have proto %d\n", __func__, tnl_type, itf->tunnel_parm6.proto); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| |
| if(tnl_type == TNL_4O6) |
| { |
| if(! itf->sam_enable) |
| { |
| update_connections = 1; |
| itf->sam_enable = 1; |
| itf->tunnel_flags |= TNL_4RD; |
| } |
| } |
| break; |
| #endif |
| |
| default: |
| cmm_print(DEBUG_ERROR, "%s: unsupported tunnel type %x\n", __func__, tnl_type); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| #ifndef SAM_LEGACY |
| if (!is_wan_port_ifindex(itf->phys_ifindex)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: Fast forward tunneling only supported on WAN interface\n", __func__); |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err1; |
| } |
| #endif |
| |
| itf->flags |= USER_ADDED; |
| |
| rc = __tunnel_add(fci_handle, itf); |
| if (rc >= 0) |
| { |
| res_buf[0] = rc; |
| rc = 0; |
| } |
| if(update_connections) |
| __itf_update_connection(fci_handle, itf->ifindex); |
| |
| err1: |
| __itf_put(itf); |
| |
| err0: |
| __pthread_mutex_unlock(&flowMutex); |
| __pthread_mutex_unlock(&neighMutex); |
| __pthread_mutex_unlock(&rtMutex); |
| __pthread_mutex_unlock(&ctMutex); |
| __pthread_mutex_unlock(&itf_table.lock); |
| |
| *res_len = 2; |
| return rc; |
| } |
| /************************************************************ |
| * |
| * __tunnel_del |
| * |
| ************************************************************/ |
| int __tunnel_del(FCI_CLIENT *fci_handle, FCI_CLIENT *fci_key_handle, struct interface *itf) |
| { |
| int rc = tunnel_send_del(fci_handle, itf); |
| |
| |
| __cmmRouteDeregister(fci_handle, &itf->rt, "tunnel"); |
| |
| if (itf->flow_orig) |
| if (!cmmFlowKeyEngineRemove(fci_key_handle, itf->flow_orig)) |
| { |
| __cmmFlowPut(itf->flow_orig); |
| |
| itf->flow_orig = NULL; |
| } |
| |
| if (itf->flow_rep) |
| if (!cmmFlowKeyEngineRemove(fci_key_handle, itf->flow_rep)) |
| { |
| __cmmFlowPut(itf->flow_rep); |
| |
| itf->flow_rep = NULL; |
| } |
| |
| return rc; |
| } |
| |
| |
| /************************************************************ |
| * |
| * tunnel_del |
| * |
| ************************************************************/ |
| static int tunnel_del(FCI_CLIENT *fci_handle, FCI_CLIENT *fci_key_handle, char *name, u_int16_t *res_buf, u_int16_t *res_len) |
| { |
| int ifindex; |
| struct interface *itf; |
| int rc = 0; |
| |
| *res_len = 2; |
| |
| __pthread_mutex_lock(&itf_table.lock); |
| __pthread_mutex_lock(&ctMutex); |
| __pthread_mutex_lock(&rtMutex); |
| __pthread_mutex_lock(&neighMutex); |
| __pthread_mutex_lock(&flowMutex); |
| |
| ifindex = if_nametoindex(name); |
| |
| itf = __itf_find(ifindex); |
| if (!itf) |
| { |
| res_buf[0] = CMMD_ERR_NOT_FOUND; |
| goto err; |
| } |
| |
| if (!__itf_is_tunnel(itf)) |
| { |
| res_buf[0] = CMMD_ERR_WRONG_COMMAND_PARAM; |
| goto err; |
| } |
| |
| #ifdef SAM_LEGACY |
| itf->tunnel_flags &= ~TNL_4RD; |
| #endif |
| rc = __tunnel_del(fci_handle, fci_key_handle, itf); |
| if (rc >= 0) |
| { |
| res_buf[0] = rc; |
| rc = 0; |
| } |
| err: |
| __pthread_mutex_unlock(&flowMutex); |
| __pthread_mutex_unlock(&neighMutex); |
| __pthread_mutex_unlock(&rtMutex); |
| __pthread_mutex_unlock(&ctMutex); |
| __pthread_mutex_unlock(&itf_table.lock); |
| |
| return rc; |
| } |
| |
| |
| /************************************************************ |
| * |
| * tunnel_show |
| * |
| ************************************************************/ |
| static int tunnel_show(FCI_CLIENT *fci_handle, char *name, u_int16_t *res_buf, u_int16_t *res_len) |
| { |
| int ifindex; |
| struct interface *itf; |
| |
| __pthread_mutex_lock(&itf_table.lock); |
| |
| ifindex = if_nametoindex(name); |
| |
| itf = __itf_find(ifindex); |
| if (!itf) |
| { |
| res_buf[0] = CMMD_ERR_NOT_FOUND; |
| *res_len = 2; |
| goto err; |
| } |
| |
| if (!__itf_is_tunnel(itf)) |
| { |
| res_buf[0] = CMMD_ERR_NOT_CONFIGURED; |
| *res_len = 2; |
| goto err; |
| } |
| |
| /* +4 is for making the structure 4-byte aligned in memory |
| * to boost access performance. It's not +0 because we need to put response code |
| * in there. |
| * TODO: this should be refactored (a response structure should be introduced) |
| */ |
| if (sizeof(struct interface) < *res_len) |
| { |
| res_buf[0] = CMMD_ERR_OK; |
| memcpy((u_int8_t*)res_buf + 4, itf, sizeof(struct interface)); |
| *res_len = sizeof(struct interface) + 4; |
| } |
| |
| tunnel_print_info(itf); |
| |
| err: |
| __pthread_mutex_unlock(&itf_table.lock); |
| return 0; |
| } |
| |
| #ifdef SAM_LEGACY |
| |
| int tunnel_conv_id_set(FCI_CLIENT *fci_handle, char *name, char *buffer, int buffer_size) |
| { |
| int ifindex; |
| struct interface *itf; |
| int rc = 0; |
| |
| pthread_mutex_lock(&itf_table.lock); |
| |
| ifindex = if_nametoindex(name); |
| |
| itf = __itf_find(ifindex); |
| if (!itf) |
| goto err; |
| |
| if (!____itf_is_4o6_tunnel(itf)) |
| goto err; |
| |
| rc = fci_write(fci_handle, FPP_CMD_TUNNEL_4rd_ID_CONV_psid, sizeof(fpp_tunnel_id_conv_cmd_t),(unsigned short *)buffer); |
| if(rc != 0) |
| cmm_print(DEBUG_ERROR, "%s: Error %d while sending FPP_CMD_TUNNEL_4rd_ID_CONV\n", __func__, rc); |
| |
| err: |
| pthread_mutex_unlock(&itf_table.lock); |
| return rc; |
| |
| } |
| |
| #endif |
| |
| /************************************************************ |
| * |
| * tunnel_daemon_msg_recv |
| * Role: Parse CMM to deamon messages |
| ************************************************************/ |
| int tunnel_daemon_msg_recv(FCI_CLIENT *fci_handle, FCI_CLIENT *fci_key_handle, int function_code, u_int8_t *cmd_buf, u_int16_t cmd_len, u_int16_t *res_buf, u_int16_t *res_len) |
| { |
| cmmd_tunnel_t *tnl = (cmmd_tunnel_t *) cmd_buf; |
| #ifdef SAM_LEGACY |
| fpp_tunnel_id_conv_cmd_t *tnl_IdConv = (fpp_tunnel_id_conv_cmd_t*) cmd_buf; |
| #endif |
| |
| switch (function_code) |
| { |
| case CMMD_CMD_TUNNEL_ADD: |
| return tunnel_add(fci_handle, tnl->name, tnl->ipsec, tnl->tunnel_type, res_buf, res_len); |
| |
| case CMMD_CMD_TUNNEL_DEL: |
| return tunnel_del(fci_handle, fci_key_handle, tnl->name, res_buf, res_len); |
| |
| case CMMD_CMD_TUNNEL_SHOW: |
| return tunnel_show(fci_handle, tnl->name, res_buf, res_len); |
| #ifdef SAM_LEGACY |
| case CMMD_CMD_TUNNEL_SAMREADY: |
| { |
| if(tnl->tun_mtu > __itf_get_mtu(if_nametoindex(tnl->name))) |
| { |
| cmm_print(DEBUG_STDERR,"\n ERROR : configured MTU cannot be greater than tunnel interface's MTU"); |
| return 0; |
| } |
| TunMtu = tnl->tun_mtu; |
| |
| if(tnl->sam_enable) |
| return tunnel_add(fci_handle, tnl->name, tnl->ipsec, tnl->tunnel_type , res_buf, res_len); |
| else |
| return tunnel_del(fci_handle, fci_key_handle, tnl->name,res_buf, res_len); |
| } |
| case CMMD_CMD_TUNNEL_IDCONV_psid: |
| { |
| res_buf[0] = CMMD_ERR_OK; |
| *res_len = 2; |
| |
| return tunnel_conv_id_set(fci_handle, (char*)tnl_IdConv->name,(char*)cmd_buf, sizeof(fpp_tunnel_id_conv_cmd_t)); |
| } |
| #endif |
| default: |
| res_buf[0] = CMMD_ERR_UNKNOWN_COMMAND; |
| *res_len = 2; |
| } |
| |
| return 0; |
| } |
| |
| static u_int32_t try_6rd(const u_int32_t *daddr, struct interface *itf) |
| { |
| u_int32_t dst = 0; |
| |
| if (itf->tunnel_flags & TNL_6RD) |
| { |
| if (cmmPrefixEqual(daddr, itf->tunnel_parm6rd.prefix.s6_addr32, itf->tunnel_parm6rd.prefixlen)) { |
| unsigned pbw0, pbi0; |
| int pbi1; |
| u_int32_t d; |
| |
| pbw0 = itf->tunnel_parm6rd.prefixlen >> 5; |
| pbi0 = itf->tunnel_parm6rd.prefixlen & 0x1f; |
| |
| d = (ntohl(daddr[pbw0]) << pbi0) >> itf->tunnel_parm6rd.relay_prefixlen; |
| |
| pbi1 = pbi0 - itf->tunnel_parm6rd.relay_prefixlen; |
| if (pbi1 > 0) |
| d |= ntohl(daddr[pbw0 + 1]) >> (32 - pbi1); |
| |
| dst = (itf->tunnel_parm6rd.relay_prefix & ((1 << itf->tunnel_parm6rd.relay_prefixlen) - 1)) | htonl(d); |
| } |
| } |
| else |
| { |
| if (((u_int16_t *)daddr)[0] == htons(0x2002)) { |
| /* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */ |
| memcpy(&dst, &((u_int16_t *)daddr)[1], 4); |
| } |
| } |
| |
| return dst; |
| } |
| |
| unsigned int tunnel_get_ipv4_dst(struct RtEntry *route, struct interface *itf) |
| { |
| unsigned int dst; |
| |
| #if defined(PROPRIETARY_6RD) |
| dst = try_6rd(route->dAddr, itf); |
| |
| if (!dst) |
| dst = try_6rd(route->gwAddr, itf); |
| |
| if (!dst) |
| dst = itf->tunnel_parm6rd.relay_prefix; |
| |
| #else |
| dst = try_6rd(route->dAddr, itf); |
| |
| /* FIXME this doesn't match exactly the Linux ipv6->ipv4 address mapping */ |
| if (!dst) |
| { |
| /* ipv6 addr compatible v4 */ |
| if ((route->gwAddr[0] == 0) && (route->gwAddr[1] == 0) && (route->gwAddr[2] == 0) && |
| route->gwAddr[3] && (route->gwAddr[3] != htonl(0x00000001))) |
| dst = route->gwAddr[3]; |
| } |
| #endif |
| |
| return dst; |
| } |
| |
| /************************************************************ |
| * |
| * __cmmGetTunnel6rd |
| * Role : Check if interface is a 6rd tunnel and retrieve info from kernel |
| ************************************************************/ |
| static void __cmmGetTunnel6rd(int fd, struct interface *itf) |
| { |
| struct ifreq ifr; |
| int rc; |
| |
| itf->tunnel_flags &= ~TNL_6RD; |
| memset(&itf->tunnel_parm6rd, 0, sizeof(struct ip_tunnel_6rd)); |
| |
| if (____itf_get_name(itf, ifr.ifr_name, sizeof(ifr.ifr_name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| |
| goto out; |
| } |
| |
| ifr.ifr_ifru.ifru_data = (void *)&itf->tunnel_parm6rd; |
| |
| rc = ioctl(fd, SIOCGET6RD, &ifr); |
| if (rc < 0) |
| goto out; |
| |
| itf->tunnel_flags |= TNL_6RD; |
| |
| out: |
| return; |
| } |
| |
| /************************************************************ |
| * |
| * __cmmGetTunnel |
| * Role : Check if interface is a tunnel and retrieve info from kernel |
| ************************************************************/ |
| int __cmmGetTunnel(int fd, struct interface *itf) |
| { |
| struct ifreq ifr; |
| int rc; |
| |
| itf->itf_flags &= ~ITF_TUNNEL; |
| memset(&itf->tunnel_parm4, 0, sizeof(struct ip6_tnl_parm)); |
| memset(&itf->tunnel_parm6, 0, sizeof(struct ip_tunnel_parm)); |
| |
| if (____itf_get_name(itf, ifr.ifr_name, sizeof(ifr.ifr_name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: ____itf_get_name(%d) failed\n", __func__, itf->ifindex); |
| |
| goto out; |
| } |
| |
| switch (itf->type) |
| { |
| case ARPHRD_ETHER: |
| if (itf->phys_ifindex == itf->ifindex) |
| goto out; |
| |
| if (!__itf_is_pointopoint(itf)) |
| goto out; |
| |
| ifr.ifr_ifru.ifru_data = (void *)&itf->tunnel_parm6; |
| |
| rc = ioctl(fd, SIOCGETTUNNEL, &ifr); |
| if (rc < 0) |
| goto out; |
| |
| /* In case this is not a real tunnel interface these should not match */ |
| if (itf->phys_ifindex != itf->tunnel_parm6.link) |
| goto out; |
| |
| if (itf->tunnel_parm6.proto == IPPROTO_ETHERIP) |
| { |
| itf->itf_flags |= ITF_TUNNEL; |
| itf->tunnel_family = AF_INET6; |
| goto out; |
| } |
| |
| break; |
| |
| case ARPHRD_TUNNEL: |
| cmm_print(DEBUG_ERROR, "%s: itf(%d) unsupported tunnel type (%x)\n", __func__, itf->ifindex, itf->type); |
| goto out; |
| |
| itf->itf_flags |= ITF_TUNNEL; |
| itf->tunnel_family = AF_INET; |
| break; |
| |
| case ARPHRD_TUNNEL6: |
| cmm_print(DEBUG_INFO, "%s: itf(%d) supported tunnel type (%x)\n", __func__, itf->ifindex, itf->type); |
| |
| /* if (!__itf_is_pointopoint(itf)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: itf(%d) is not point to point and tunnel's remote address is not configured\n", __func__, itf->ifindex); |
| goto out; |
| }*/ |
| |
| ifr.ifr_ifru.ifru_data = (void *)&itf->tunnel_parm6; |
| |
| rc = ioctl(fd, SIOCGETTUNNEL, &ifr); |
| if (rc < 0) |
| goto out; |
| |
| cmm_print(DEBUG_INFO, "%s: itf(%d) tunnel flag is set (%x)\n", __func__, itf->ifindex, itf->flags); |
| itf->itf_flags |= ITF_TUNNEL; |
| itf->tunnel_family = AF_INET6; |
| /* Add this type of tunnel automatically */ |
| itf->flags |= USER_ADDED; |
| break; |
| |
| case ARPHRD_SIT: |
| ifr.ifr_ifru.ifru_data = (void *)&itf->tunnel_parm4; |
| |
| rc = ioctl(fd, SIOCGETTUNNEL, &ifr); |
| if (rc < 0) |
| goto out; |
| |
| __cmmGetTunnel6rd(fd, itf); |
| |
| itf->itf_flags |= ITF_TUNNEL; |
| itf->tunnel_family = AF_INET; |
| |
| /* Add this type of tunnel automatically */ |
| itf->flags |= USER_ADDED; |
| |
| break; |
| |
| case ARPHRD_IPGRE: |
| cmm_print(DEBUG_ERROR, "%s: itf(%d) unsupported tunnel type (%x)\n", __func__, itf->ifindex, itf->type); |
| goto out; |
| |
| itf->itf_flags |= ITF_TUNNEL; |
| itf->tunnel_family = AF_INET; |
| break; |
| |
| #ifndef ARPHRD_NONE |
| #define ARPHRD_NONE 0xFFFE |
| #endif |
| case ARPHRD_NONE: /* As is the case for tun/tap interfaces */ |
| cmm_print(DEBUG_ERROR, "%s: itf(%d) supported tunnel type (%x)\n", __func__, itf->ifindex, itf->type); |
| ifr.ifr_ifru.ifru_data = (void *)&itf->tunnel_parm6; |
| |
| rc = ioctl(fd, SIOCGIFFLAGS, &ifr); |
| cmm_print(DEBUG_ERROR, "%s: itf(%d) rc is %d \n", __func__, itf->ifindex, rc); |
| if (rc < 0) |
| goto out; |
| if(!(ifr.ifr_flags & IFF_TUN)) |
| { |
| cmm_print(DEBUG_ERROR, "%s: itf(%d) is not a TUN interface \n", __func__, itf->ifindex); |
| goto out; |
| } |
| |
| itf->itf_flags |= ITF_TUNNEL; |
| itf->tunnel_family = globalConf.tun_family; |
| itf->tunnel_parm6.proto = globalConf.tun_proto; |
| cmm_print(DEBUG_INFO,"%s: tun family is %d,tun proto is %d\n",__func__, globalConf.tun_family,globalConf.tun_proto); |
| /* Add this type of tunnel automatically */ |
| itf->flags |= USER_ADDED; |
| break; |
| |
| |
| default: |
| break; |
| } |
| |
| out: |
| return 0; |
| } |
| |
| /************************************************************ |
| * |
| * __cmmTunnelUpdateWithRoute |
| * |
| ************************************************************/ |
| void __cmmTunnelUpdateWithRoute(FCI_CLIENT *fci_handle, struct RtEntry *route) |
| { |
| struct interface *itf; |
| struct list_head *entry; |
| struct fpp_rt *fpp_route; |
| int i; |
| |
| for (i = 0; i < ITF_HASH_TABLE_SIZE; i++) |
| { |
| for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry)) |
| { |
| itf = container_of(entry, struct interface, list); |
| |
| if (!__itf_is_tunnel(itf)) |
| continue; |
| |
| if (itf->rt.route == route) |
| { |
| fpp_route = itf->rt.fpp_route; |
| itf->rt.fpp_route = NULL; |
| |
| __tunnel_add(fci_handle, itf); |
| |
| __cmmFPPRouteDeregister(fci_handle, fpp_route, "tunnel"); |
| } |
| } |
| } |
| } |
| |
| /************************************************************ |
| * |
| * __cmmTunnelFindFromFlow |
| * Role : Finds tunnel entry that matches flow |
| ************************************************************/ |
| struct interface *__cmmTunnelFindFromFlow(int family, unsigned int *saddr, unsigned int *daddr, unsigned char proto, char *orig) |
| { |
| struct interface *itf; |
| struct list_head *entry; |
| int i; |
| |
| for (i = 0; i < ITF_HASH_TABLE_SIZE; i++) |
| { |
| for (entry = list_first(&itf_table.hash[i]); entry != &itf_table.hash[i]; entry = list_next(entry)) |
| { |
| itf = container_of(entry, struct interface, list); |
| |
| if (!__itf_is_tunnel(itf)) |
| continue; |
| |
| if (itf->tunnel_family != family) |
| continue; |
| |
| if (!(itf->tunnel_flags & TNL_IPSEC)) |
| continue; |
| |
| if (family == AF_INET6) |
| { |
| if (!memcmp(saddr, itf->tunnel_parm6.laddr.s6_addr, 16) |
| && !memcmp(daddr, itf->tunnel_parm6.raddr.s6_addr, 16) |
| && (proto == itf->tunnel_parm6.proto)) |
| { |
| *orig = 1; |
| goto found; |
| } |
| |
| if (!memcmp(daddr, itf->tunnel_parm6.laddr.s6_addr, 16) |
| && !memcmp(saddr, itf->tunnel_parm6.raddr.s6_addr, 16) |
| && (proto == itf->tunnel_parm6.proto)) |
| { |
| *orig = 0; |
| goto found; |
| } |
| } |
| else |
| { |
| } |
| } |
| } |
| |
| itf = NULL; |
| |
| found: |
| return itf; |
| } |
| |
| |
| #ifdef SAM_LEGACY |
| |
| int cmm4rdIdConvSetProcess(char ** keywords, int tabStart, int argc, daemon_handle_t daemon_handle) |
| { |
| int rcvBytes = 0; |
| char SndBuffer[256]; |
| if(argc < 3) |
| goto usage; |
| |
| fpp_tunnel_id_conv_cmd_t *pIdConvCmd = (fpp_tunnel_id_conv_cmd_t* )SndBuffer; |
| memset(pIdConvCmd,0, sizeof(fpp_tunnel_id_conv_cmd_t)); |
| if(strcasecmp(keywords[tabStart++],"interface") != 0) |
| goto usage; |
| |
| strncpy((char*)pIdConvCmd->name, keywords[tabStart++],IFNAMSIZ); |
| if(strcasecmp(keywords[tabStart++],"enable") == 0) |
| pIdConvCmd->IdConvStatus = 1; |
| if(rt_mw_sam_get_portsetid(&pIdConvCmd->sam_port_info)) |
| { |
| cmm_print(DEBUG_STDOUT,"\t Third party shared library failed \n"); |
| goto usage; |
| } |
| rcvBytes = cmmSendToDaemon(daemon_handle,CMMD_CMD_TUNNEL_IDCONV_psid, pIdConvCmd, sizeof(fpp_tunnel_id_conv_cmd_t), SndBuffer); |
| if (rcvBytes >=2)/* we expect 2 bytes in response */ |
| { |
| if ((((u_int16_t*)SndBuffer)[0]) != CMMD_ERR_OK) |
| { |
| cmm_print(DEBUG_STDERR, "Error %d received from CMM Deamon for CMMD_CMD_CMMTD_TUNNEL_IDCONV\n", ((u_int16_t*)SndBuffer)[0]); |
| return -1; |
| } |
| } |
| |
| |
| return 0; |
| usage: |
| cmm_print(DEBUG_STDOUT,"\tset 4rd-id-conversion interface <4o6 Interface name> <enable/disable>\n"); |
| return -1; |
| |
| } |
| |
| #else |
| int cmm4rdIdConvSetProcess(char ** keywords, int tabStart, int argc, daemon_handle_t daemon_handle) |
| { |
| int rcvBytes = 0,rc =0; |
| char SndBuffer[256]; |
| fpp_tunnel_id_conv_cmd_t *pIdConvCmd = (fpp_tunnel_id_conv_cmd_t* )SndBuffer; |
| |
| if(!keywords[tabStart]) |
| goto usage; |
| memset(pIdConvCmd,0, sizeof(fpp_tunnel_id_conv_cmd_t)); |
| if(strcasecmp(keywords[tabStart],"enable") == 0) |
| pIdConvCmd->IdConvStatus = 1; |
| rcvBytes = cmmSendToDaemon(daemon_handle, FPP_CMD_TUNNEL_4rd_ID_CONV_dport, pIdConvCmd, sizeof(fpp_tunnel_id_conv_cmd_t), SndBuffer); |
| rc = (rcvBytes < sizeof(unsigned short) ) ? 0 : *((unsigned short *) SndBuffer); |
| if (rcvBytes != sizeof(unsigned short) || (rc)) |
| cmm_print(DEBUG_STDERR, "ERROR: Unexpected result returned from FPP rc:%04x\n",rc ); |
| return rc; |
| usage: |
| cmm_print(DEBUG_STDOUT,"\tset 4rd-id-conversion <enable/disable>\n"); |
| return -1; |
| |
| } |
| #endif |
| |
| int cmmTnlQueryProcess(char ** keywords, int tabStart, daemon_handle_t daemon_handle) |
| { |
| int rcvBytes = 0; |
| char rcvBuffer[256]; |
| short rc; |
| int count = 0; |
| char local[INET6_ADDRSTRLEN], remote[INET6_ADDRSTRLEN]; |
| cmmd_tunnel_query_cmd_t* pTnlCmd = (cmmd_tunnel_query_cmd_t*) rcvBuffer; |
| |
| rcvBytes = cmmSendToDaemon(daemon_handle, FPP_CMD_TUNNEL_QUERY, pTnlCmd, |
| sizeof(cmmd_tunnel_query_cmd_t) , rcvBuffer); |
| |
| if (rcvBytes != sizeof(cmmd_tunnel_query_cmd_t) ) { |
| rc = (rcvBytes < sizeof(unsigned short) ) ? 0 : |
| *((unsigned short *) rcvBuffer); |
| if (rc == FPP_ERR_UNKNOWN_ACTION) { |
| cmm_print(DEBUG_STDERR, |
| "ERROR: FPP Tunnel does not support ACTION_QUERY\n"); |
| } else if (rc == FPP_ERR_TNL_ENTRY_NOT_FOUND) { |
| cmm_print(DEBUG_STDERR, "ERROR: FPP Tunnel table empty\n"); |
| } else { |
| cmm_print(DEBUG_STDERR, |
| "ERROR: Unexpected result returned from FPP rc:%d\n", rc); |
| } |
| return CLI_OK; |
| } |
| |
| cmm_print(DEBUG_STDOUT, "Tunnel interfaces:\n"); |
| do { |
| if (pTnlCmd->mode == TNL_4O6) |
| { |
| cmm_print(DEBUG_STDOUT, |
| "%d: Mode: 4o6 Interface name: %s local: %s remote :%s ",count, pTnlCmd->name, inet_ntop(AF_INET6 , &pTnlCmd->local , local, INET6_ADDRSTRLEN), inet_ntop(AF_INET6 , &pTnlCmd->remote , remote, INET6_ADDRSTRLEN) ); |
| } |
| else if (pTnlCmd->mode == TNL_6O4) |
| { |
| cmm_print(DEBUG_STDOUT, |
| "%d: Mode: 6o4 Interface name: %s local: %s remote :%s ", count, pTnlCmd->name, inet_ntop(AF_INET , &pTnlCmd->local , local, INET6_ADDRSTRLEN), inet_ntop(AF_INET , &pTnlCmd->remote , remote, INET6_ADDRSTRLEN) ); |
| } |
| else |
| { |
| cmm_print(DEBUG_STDOUT, |
| "%d: Mode: Etherip Interface name: %s local: %s remote :%s ",count, pTnlCmd->name, inet_ntop(AF_INET6 , &pTnlCmd->local , local, INET6_ADDRSTRLEN), inet_ntop(AF_INET6 , &pTnlCmd->remote , remote, INET6_ADDRSTRLEN) ); |
| } |
| cmm_print(DEBUG_STDOUT, "enabled: %d secure : %d fl :%x encap_limit: %x hop_limit:%x" , pTnlCmd->enabled, pTnlCmd->secure, pTnlCmd->flow_info , pTnlCmd->encap_limit, pTnlCmd->hop_limit); |
| cmm_print(DEBUG_STDOUT,"\n"); |
| |
| count++; |
| rcvBytes = cmmSendToDaemon(daemon_handle, FPP_CMD_TUNNEL_QUERY_CONT, pTnlCmd, sizeof(cmmd_tunnel_query_cmd_t) , rcvBuffer); |
| }while (rcvBytes == sizeof(cmmd_tunnel_query_cmd_t) ); |
| cmm_print(DEBUG_STDOUT, "Total Tunnel Entries:%d\n", count); |
| |
| return CLI_OK; |
| } |
| |
| #ifdef SAM_LEGACY |
| |
| int getTunnel4rdAddress(struct interface* itf, u_int32_t * Daddrv6, unsigned int Daddr, unsigned short Dport) |
| { |
| int i =0; |
| for (i = 0; i< 4;i++) |
| Daddrv6[i] = itf->tunnel_parm6.raddr.s6_addr32[i]; |
| rt_mw_sam_make_dst_ipv6( (struct in_addr *)&Daddr , Dport, (struct in6_addr *)Daddrv6 ); |
| return 0; |
| } |
| #else |
| |
| static int |
| __getTunnel4rdAddress(__u32 *daddr6, __u32 daddr4, __u16 dport4, struct ip6_4rd_map_msg *mr) |
| { |
| |
| int i, pbw0, pbi0, pbi1; |
| __u32 daddr[4]; |
| __u32 port_set_id = 0; |
| __u32 mask; |
| __u32 da = ntohl(daddr4); |
| __u16 dp = ntohs(dport4); |
| __u32 diaddr[4]; |
| int port_set_id_len = ( mr->eabit_len ) - ( 32 - mr->prefixlen ) ; |
| |
| if ( port_set_id_len < 0) { |
| cmm_print(DEBUG_STDOUT," %s: PSID length ERROR %d\n",__func__, port_set_id_len); |
| return -1; |
| } |
| |
| if ( port_set_id_len > 0) { |
| mask = 0xffffffff >> (32 - port_set_id_len); |
| port_set_id = ( dp >> (16 - mr->psid_offsetlen - port_set_id_len ) & mask ) ; |
| } |
| |
| for (i = 0; i < 4; ++i) |
| daddr[i] = ntohl(mr->relay_prefix.s6_addr32[i]) |
| | ntohl(mr->relay_suffix.s6_addr32[i]); |
| |
| if( port_set_id_len != 0 ) { |
| pbw0 = mr->relay_prefixlen >> 5; |
| pbi0 = mr->relay_prefixlen & 0x1f; |
| daddr[pbw0] |= (da << mr->prefixlen) >> pbi0; |
| pbi1 = pbi0 - mr->prefixlen; |
| if (pbi1 > 0) |
| daddr[pbw0+1] |= da << (32 - pbi1); |
| |
| if ( port_set_id_len > 0) { |
| pbw0 = (mr->relay_prefixlen + 32 - mr->prefixlen) >> 5; |
| pbi0 = (mr->relay_prefixlen + 32 - mr->prefixlen) & 0x1f; |
| daddr[pbw0] |= (port_set_id << (32 - port_set_id_len)) >> pbi0; |
| pbi1 = pbi0 - (32 - port_set_id_len); |
| if (pbi1 > 0) |
| daddr[pbw0+1] |= port_set_id << (32 - pbi1); |
| } |
| } |
| |
| memset(diaddr, 0, sizeof(diaddr)); |
| |
| diaddr[2] = ( da >> 8 ) ; |
| diaddr[3] = ( da << 24 ) ; |
| diaddr[3] |= ( port_set_id << 8 ) ; |
| |
| for (i = 0; i < 4; ++i) |
| daddr[i] = daddr[i] | diaddr[i] ; |
| |
| for (i = 0; i < 4; ++i) |
| daddr6[i] = htonl(daddr[i]); |
| |
| /* DBG */ |
| cmm_print(DEBUG_INFO," %s : %08x %08x %08x %08x PSID:%04x\n",__func__ ,daddr[0], daddr[1], daddr[2], daddr[3], port_set_id); |
| /* DBG */ |
| |
| return 0; |
| } |
| |
| |
| |
| int getTunnel4rdAddress(struct interface* itf, u_int32_t * Daddrv6, unsigned int Daddr, unsigned short Dport) |
| { |
| |
| struct list_head *entry, *next_entry; |
| struct map_rule *mr = NULL, *mr_tmp = NULL; |
| unsigned int mask = 0; |
| int mr_prefixlen = 0; |
| int count = 0; |
| int err = 0; |
| int i = 0; |
| // set default daddr as that of tunnel remote address will be used for all 4o6 tunnels and when packets are intended for BR/ P-SAM. |
| for (i = 0; i< 4;i++) |
| Daddrv6[i] = itf->tunnel_parm6.raddr.s6_addr32[i]; |
| |
| |
| cmm_print(DEBUG_INFO, "%s: mapping rule match \n", __func__); |
| |
| for (entry = list_first(&itf->mr_list); next_entry = list_next(entry), entry != &itf->mr_list; entry = next_entry) |
| { |
| |
| mr = container_of(entry, struct map_rule, list); |
| mask = 0xffffffff << (32 - mr->rule.prefixlen) ; |
| cmm_print(DEBUG_INFO,"Prefix %d prefixlen %d daddr %d ",htonl(mr->rule.prefix), mr->rule.prefixlen, htonl(Daddr)); |
| if( (htonl(Daddr) & mask ) == htonl( mr->rule.prefix) ) { |
| if ( mr->rule.prefixlen >= mr_prefixlen ){ |
| mr_prefixlen = mr->rule.prefixlen ; |
| mr_tmp = mr; |
| count++; |
| } |
| } |
| } |
| if(count) |
| err =__getTunnel4rdAddress(Daddrv6, Daddr, Dport, &mr_tmp->rule ); |
| return err; |
| } |
| |
| |
| #endif |