/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmcp.c,v $ $Revision: 1.30.10.4 $ * $Source: /home/interpeak/CVSRoot/ipmcp/src/ipmcp.c,v $ * $Author: jren $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipmcp_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include "ipmcp.h" #include "ipmcp_h.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IPCOM_PROCESS(ipmcp_daemon); /* **************************************************************************** * 8 DATA **************************************************************************** */ Ipmcp_data_t ipmcp; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmcp_if_destroy *=========================================================================== * Description: Frees all resources held by the interface structure. * Parameters: * Returns: * */ IP_STATIC void ipmcp_if_destroy(Ipmcp_netif_t *netif) { if (netif == IP_NULL) return; #ifdef IPMCP_USE_IGMP ipmcp_igmp_if_destroy(netif); #endif #ifdef IPMCP_USE_MLD ipmcp_mld_if_destroy(netif); #endif ipcom_list_remove(&netif->list); ipcom_free(netif); } /* *=========================================================================== * ipmcp_if_create *=========================================================================== * Description: Create interface descriptors for IPMCP * Parameters: * Returns: * */ IP_STATIC int ipmcp_if_create(Ip_u32 ifindex) { Ipmcp_netif_t *netif; int ret; char ifname[IP_IFNAMSIZ]; char *downstream_if; char *p = IP_NULL; char *sep = ", "; char *ds; netif = ipmcp_if_find(ifindex); if (netif) /* Duplicate */ return -1; if (ipcom_if_indextoname(ifindex, ifname) == IP_NULL) { IPCOM_LOG1(ERR, "ipmcp_if_create: could not get the name of the interface with index %d", ifindex); return -1; } ds = ipcom_strdup(ipmcp.config.downstream_ifs); if (ds == IP_NULL) { IPCOM_LOG1(ERR, "ipmcp_if_create: failed to duplicate string", ifindex); return -1; } for (downstream_if = ipcom_strtok_r(ds, sep, &p); downstream_if != IP_NULL; downstream_if = ipcom_strtok_r(IP_NULL, sep, &p)) { if (ipcom_strcmp(ifname, downstream_if) == 0) break; } ipcom_free(ds); if (downstream_if == IP_NULL) { IPCOM_LOG1(DEBUG, "ipmcp_if_create: interface %s is not a downstream interface, ignoring", ifname); return 0; } netif = ipcom_calloc(1, sizeof(Ipmcp_netif_t)); if (netif == IP_NULL) { IPCOM_LOG0(ERR, "ipmcp_if_create: failed to allocate memory"); return -1; } netif->ifindex = ifindex; ipcom_strcpy(netif->ifname, ifname); ipcom_list_insert_last(&ipmcp.downstream_ifs_head, &netif->list); #ifdef IPMCP_USE_IGMP ret = ipmcp_igmp_if_create(netif); if (ret < 0) goto cleanup; #endif #ifdef IPMCP_USE_MLD ret = ipmcp_mld_if_create(netif); if (ret < 0) goto cleanup; #endif return 0; cleanup: ipmcp_if_destroy(netif); return ret; } /* *=========================================================================== * ipmcp_add_downstream_ifs *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipmcp_add_downstream_ifs(void) { struct Ip_if_nameindex *ifrs, *i; struct Ip_ifreq ifreq; int ret = -1; ifrs = ipcom_if_nameindex(); for (i = ifrs; i && (i->if_name != IP_NULL); i++) { /* coverity[fixed_size_dest] : FALSE */ ipcom_strcpy(ifreq.ifr_name, i->if_name); #ifdef IP_PORT_LAS if (ipcom_socketioctl(ipmcp.cfg_fd, IP_SIOCGIFFLAGS, (char*)&ifreq) < 0) #else if (ipcom_socketioctl(ipmcp.ipd_fd, IP_SIOCGIFFLAGS, (char*)&ifreq) < 0) #endif { IPCOM_LOG1(ERR, "ipcom_ioctl(SIOCGIFFLAGS) failed, errno = %d", ipcom_errno); goto cleanup; } /* Skip loopback */ if (IP_BIT_ISSET(ifreq.ifr_ifru.ifru_flags, IP_IFF_LOOPBACK)) continue; /* Skip if not multicast capable */ if (IP_BIT_ISFALSE(ifreq.ifr_ifru.ifru_flags, IP_IFF_MULTICAST)) continue; /* Skip if DOWN */ if (IP_BIT_ISFALSE(ifreq.ifr_ifru.ifru_flags, IP_IFF_UP)) continue; if (ipmcp_if_create(i->if_index) < 0) goto cleanup; } ret = 0; cleanup: ipcom_if_freenameindex(ifrs); return ret; } /* *=========================================================================== * ipmcp_upstream_if_add *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipmcp_upstream_if_add(void) { struct Ip_ifreq ifreq; Ip_size_t namsiz = sizeof(ifreq.ifr_name); int ret; if (ipcom_sysvar_get("ipmcp.UpstreamIf", ifreq.ifr_name, &namsiz) == IP_NULL) { IPCOM_LOG0(ERR, "missing 'ipmcp.UpstreamIf'"); IP_PANIC2(); return -1; } ipcom_strcpy(ipmcp.upstream_if.ifname, ifreq.ifr_name); ipmcp.upstream_if.ifindex = ipcom_if_nametoindex(ifreq.ifr_name); if (ipmcp.upstream_if.ifindex == 0) { IPCOM_LOG1(ERR, "upstream interface %s is not attached to the stack", ifreq.ifr_name); IP_PANIC2(); return -1; } #ifdef IP_PORT_LAS if (ipcom_socketioctl(ipmcp.cfg_fd, IP_SIOCGIFFLAGS, (char*)&ifreq) < 0) #else if (ipcom_socketioctl(ipmcp.ipd_fd, IP_SIOCGIFFLAGS, (char*)&ifreq) < 0) #endif { IPCOM_LOG1(ERR, "ipcom_ioctl(SIOCGIFFLAGS) failed: %s", ipcom_strerror(ipcom_errno)); IP_PANIC(); return -1; } if (IP_BIT_ISSET(ifreq.ifr_ifru.ifru_flags, IP_IFF_LOOPBACK) || IP_BIT_ISFALSE(ifreq.ifr_ifru.ifru_flags, IP_IFF_MULTICAST)) { IPCOM_LOG1(ERR, "%s cannot be used as uplink interface, must not be loopback and must be multicast capable", ifreq.ifr_name); IP_PANIC2(); return -1; } if (IP_BIT_ISSET(ifreq.ifr_ifru.ifru_flags, IP_IFF_UP)) ipmcp.state = IPMCP_STATE_RUNNING; else { IPCOM_LOG1(NOTICE, "%s was found, but is currently not UP, enter WAITING state", ifreq.ifr_name); ipmcp.state = IPMCP_STATE_WAITING; } #ifdef IPMCP_USE_IGMP ret = ipmcp_igmp_upstream_if_create(&ipmcp.upstream_if); if (ret < 0) return ret; #endif #ifdef IPMCP_USE_MLD ret = ipmcp_mld_upstream_if_create(&ipmcp.upstream_if); if (ret < 0) return ret; #endif return 0; } /* *=========================================================================== * ipmcp_upstream_if_destroy *=========================================================================== * Description: Removes the upstream interface. * Parameters: * Returns: 0 = success, <0 = error. * */ IP_STATIC void ipmcp_upstream_if_destroy(void) { #ifdef IPMCP_USE_IGMP ipmcp_igmp_upstream_if_destroy(&ipmcp.upstream_if); #endif #ifdef IPMCP_USE_MLD ipmcp_mld_upstream_if_destroy(&ipmcp.upstream_if); #endif } /* *=========================================================================== * ipmcp_apply_configuration *=========================================================================== * Description: Setup the system according to the configuration. * Parameters: * Returns: 0 = success, <0 = error. * */ IP_STATIC int ipmcp_setup_configuration(void) { Ipmcp_conf_t *conf = &ipmcp.config; if (ipmcp_upstream_if_add() < 0) return -1; /* Read sysvar configuration. */ conf->robustness_variable = (Ip_u32)ipcom_sysvar_get_as_int("ipmcp.RobustnessVariable", 2); if (conf->robustness_variable == 0 || conf->robustness_variable == 0xffffffff) { IPCOM_LOG0(ERR, "invalid 'ipmcp.RobustnessVariable'"); return -1; } conf->query_interval = (Ip_u32)ipcom_sysvar_get_as_int("ipmcp.QueryInterval", 125); if (conf->query_interval > 0xffffffff / 1000) { IPCOM_LOG1(ERR, "No support for query intervals > %u 'ipmcp.QueryInterval'", 0xffffffff / 1000); return -1; } conf->query_interval *= 1000; /* milliseconds */ conf->query_response_interval = (Ip_u16)ipcom_sysvar_get_as_int("ipmcp.QueryResponseInterval", 10000); conf->last_member_query_interval = (Ip_u32)ipcom_sysvar_get_as_int("ipmcp.LastMemberQueryInterval", 1000); conf->last_member_query_count = (Ip_u32)ipcom_sysvar_get_as_int("ipmcp.LastMemberQueryCount", conf->robustness_variable); /* Calculate some parameters */ conf->startup_query_interval = (Ip_u32)ipcom_sysvar_get_as_int("ipmcp.StartupQueryInterval", 0) * 1000; if (conf->startup_query_interval == 0) conf->startup_query_interval = conf->query_interval / 4; conf->startup_query_count = conf->robustness_variable; ipmcp.config.downstream_ifs = ipcom_sysvar_get("ipmcp.DownstreamIfs", IP_NULL, IP_NULL); if (ipmcp.config.downstream_ifs == IP_NULL) { IPCOM_LOG0(ERR, "missing 'ipmcp.DownstreamIfs'"); return -1; } return ipmcp_add_downstream_ifs(); } /* *=========================================================================== * ipmcp_teardown_configuration *=========================================================================== * Description: Frees all resouces that is tied to the current configuration. * Parameters: * Returns: * */ IP_STATIC void ipmcp_teardown_configuration(void) { Ipmcp_netif_t *i; ipmcp.state = IPMCP_STATE_HALTED; for (i = IPCOM_LIST_FIRST(&ipmcp.downstream_ifs_head); i != IP_NULL; i = IPCOM_LIST_FIRST(&ipmcp.downstream_ifs_head)) { ipmcp_if_destroy(i); } ipmcp_upstream_if_destroy(); ipcom_free(ipmcp.config.downstream_ifs); } /* *=========================================================================== * ipmcp_init *=========================================================================== * Description: Initializes the multicast proxy. * Parameters: * Returns: 0 = success, <0 = error. * */ IP_STATIC int ipmcp_init(void) { int ret; Ip_err err = IPCOM_ERR_FAILED;; ipcom_list_init(&ipmcp.downstream_ifs_head); ipmcp.tmo_head = ipcom_tmo2_new(); if (ipmcp.tmo_head == IP_NULL) { IPCOM_LOG0(ERR, "Failed to create timeout head"); goto done; } ipmcp.rt_fd = ipcom_socket(IP_AF_ROUTE, IP_SOCK_RAW, 0); if (ipmcp.rt_fd == IP_INVALID_SOCKET) { IPCOM_LOG1(ERR, "ipcom_socket(ROUTE,RAW) failed: %s", ipcom_strerror(ipcom_errno)); goto done; } #ifdef IP_PORT_LAS ipmcp.cfg_fd = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0); if (ipmcp.cfg_fd == IP_INVALID_SOCKET) { IPCOM_LOG1(ERR, "ipcom_socket(INET,DGRAM) failed: %s", ipcom_strerror(ipcom_errno)); goto done; } #endif #ifdef IPMCP_USE_IGMP if (0 > (ret = ipmcp_igmp_init())) return ret; #endif #ifdef IPMCP_USE_MLD if (0 > (ret = ipmcp_mld_init())) return ret; #endif err = IPCOM_SUCCESS;; done: if (ipcom_ipd_init(IPMCP_IPD_NAME, err, &ipmcp.ipd_fd) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "Failed to initialize IPD: %s", ipcom_strerror(ipcom_errno)); return -1; } return 0; } /* *=========================================================================== * ipmcp_exit *=========================================================================== * Description: Frees all resources held by the daemon. * Parameters: * Returns: * */ IP_STATIC void ipmcp_exit(void) { (void)ipcom_ipd_exit("ipmcp", ipmcp.ipd_fd); (void)ipcom_socketclose(ipmcp.rt_fd); #ifdef IP_PORT_LAS (void)ipcom_socketclose(ipmcp.cfg_fd); #endif #ifdef IPMCP_USE_IGMP ipmcp_igmp_exit(); #endif #ifdef IPMCP_USE_MLD ipmcp_mld_exit(); #endif ipcom_tmo2_delete(ipmcp.tmo_head); ipmcp_is_enabled = IP_FALSE; } /* *=========================================================================== * ipmcp_ipd_input *=========================================================================== * Description: Processes IPD messages, * Parameters: fd - The IPD socket * done - Will be set to true if the daemon should shutdown. * Returns: 0 = Daemon will continue and process other messages. * 1 = The daemon must reconfigure itsef. * */ IP_STATIC int ipmcp_ipd_input(int fd, Ip_bool *done) { switch (ipcom_ipd_input(fd)) { case IPCOM_IPD_EVENT_RECONFIGURE: IPCOM_LOG0(INFO, "received IPD reconfigure message"); *done = IP_FALSE; return 1; case IPCOM_IPD_EVENT_KILL: IPCOM_LOG0(INFO, "received IPD kill message"); *done = IP_TRUE; return 1; case IPMCP_IPD_EVENT_WAIT_FOR_SHELL_CMD: /* Notify the shell command that the MCP daemon now waiting here */ (void)ipcom_sem_post(ipmcp.cmd_sem); /* This will block this task until the shell command has finished reading the state of the MCP daemon */ (void)ipcom_mutex_lock(ipmcp.daemon_mutex); (void)ipcom_mutex_unlock(ipmcp.daemon_mutex); return 0; default: return 0; } } /* *=========================================================================== * ipmcp_route_input *=========================================================================== * Description: Processes route socket messages. * Parameters: fd - The route socket * Returns: 0 = success * <0 = error * */ IP_STATIC int ipmcp_route_input(int fd) { Ipmcp_netif_t *netif; int length; union rtmsg_union { struct Ipnet_rt_msghdr rtmsg; struct Ipnet_if_msghdr ifmsg; struct Ipnet_ifa_msghdr ifamsg; } buf; /* Read routing message headers only (truncated read). */ length = ipcom_recv(fd, &buf, sizeof(buf), 0); if (length == IP_SOCKERR) { IP_PANIC2(); IPCOM_LOG1(ERR, "ipmcp_route_input: ipcom_recv() failed, errno = %d", ipcom_errno); return -1; } if (length < (int) sizeof(struct Ipnet_rs_msghdr)) { IP_PANIC2(); IPCOM_LOG2(ERR, "ipmcp_route_input: ipcom_recv() failed, truncated rtsock message %ld/%d", length, (int)sizeof(struct Ipnet_rs_msghdr)); return -1; } switch (buf.rtmsg.rtm_type) { case IPNET_RTM_DELADDR: case IPNET_RTM_NEWADDR: netif = ipmcp_if_find(buf.ifamsg.ifam_index); if (netif) { #ifdef IPMCP_USE_IGMP ipmcp_igmp_addr_change(netif, buf.rtmsg.rtm_type == IPNET_RTM_NEWADDR); #endif #ifdef IPMCP_USE_MLD ipmcp_mld_addr_change(netif, buf.rtmsg.rtm_type == IPNET_RTM_NEWADDR); #endif } break; case IPNET_RTM_IFINFO: if (IP_BIT_ISSET(buf.ifmsg.ifm_flags, IP_IFF_UP)) (void) ipmcp_if_create(buf.ifmsg.ifm_index); else ipmcp_if_destroy(ipmcp_if_find(buf.ifmsg.ifm_index)); break; default: /* ignore all other routing messages. */ break; } return 0; } /* *=========================================================================== * ipmcp_daemon *=========================================================================== * Description: Multicast proxy daemon. * Parameters: * Returns: * */ IPCOM_PROCESS(ipmcp_daemon) { int max_fd; int num; int ret = 0; Ip_fd_set read_set; int retries; Ip_bool done = IP_FALSE; ipcom_proc_init(); IPCOM_LOG0(INFO, "Multicast proxy daemon starting"); ret = ipmcp_init(); if (0 > ret) done = IP_TRUE; while (!done) { ret = ipmcp_setup_configuration(); if (ret != 0) break; ipmcp_is_enabled = IP_TRUE; for (;;) { IP_FD_ZERO(&read_set); IP_FD_SET(ipmcp.ipd_fd, &read_set); IP_FD_SET(ipmcp.rt_fd, &read_set); max_fd = IP_MAX(ipmcp.rt_fd, ipmcp.ipd_fd); #ifdef IPMCP_USE_IGMP IP_FD_SET(ipmcp.igmp.fd, &read_set); max_fd = IP_MAX(ipmcp.igmp.fd, max_fd); #endif #ifdef IPMCP_USE_MLD IP_FD_SET(ipmcp.mld.mld_fd, &read_set); max_fd = IP_MAX(ipmcp.mld.mld_fd, max_fd); IP_FD_SET(ipmcp.mld.mrt_fd, &read_set); max_fd = IP_MAX(ipmcp.mld.mrt_fd, max_fd); #endif done = IP_TRUE; for (retries = 0, num = 0; num <= 0 && retries < 3; retries++) { num = ipcom_tmo2_select(ipmcp.tmo_head, max_fd + 1, &read_set, IP_NULL, IP_NULL, IP_NULL); if (num < 0) IPCOM_LOG1(WARNING, "socket select returned %s", ipcom_strerror(ipcom_errno)); } if (num < 0) { IPCOM_LOG1(ERR, "socket select returned %s, shutting down", ipcom_strerror(ipcom_errno)); break; } if (IP_FD_ISSET(ipmcp.ipd_fd, &read_set) && ipmcp_ipd_input(ipmcp.ipd_fd, &done) != 0) break; if (IP_FD_ISSET(ipmcp.rt_fd, &read_set) && ipmcp_route_input(ipmcp.rt_fd) < 0) break; #ifdef IPMCP_USE_IGMP if (IP_FD_ISSET(ipmcp.igmp.fd, &read_set) && ipmcp_igmp_input(ipmcp.igmp.fd) < 0) break; #endif #ifdef IPMCP_USE_MLD if (IP_FD_ISSET(ipmcp.mld.mld_fd, &read_set) && ipmcp_mld_input(ipmcp.mld.mld_fd) < 0) break; if (IP_FD_ISSET(ipmcp.mld.mrt_fd, &read_set) && ipmcp_mld_mroute_input(ipmcp.mld.mrt_fd) < 0) break; #endif done = IP_FALSE; } ipmcp_teardown_configuration(); } ipmcp_exit(); IPCOM_LOG0(INFO, "Multicast proxy daemon ending"); ipcom_proc_exit(); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmcp_if_find *=========================================================================== * Description: Finds the interface that matches the index. * Parameters: * Returns: The interface structure or IP_NULL if it does not exists. * */ IP_GLOBAL Ipmcp_netif_t * ipmcp_if_find(Ip_u32 ifindex) { Ipmcp_netif_t *i; for (i = IPCOM_LIST_FIRST(&ipmcp.downstream_ifs_head); i != IP_NULL; i = IPCOM_LIST_NEXT(&i->list)) { if (ifindex == i->ifindex) return i; } return IP_NULL; } /* *=========================================================================== * ipmcp_route_init_addrs *=========================================================================== * Description: Sets the address pointer to point to the correct entry * in the 'rt' buffer. * Parameters: msg - The first socket address in the route socket message. * addrs - The IPNET_RTA_xxx from the message. * sas - Will contain the route pointer. * Returns: * */ IP_GLOBAL void ipmcp_route_init_addrs(void *msg, Ip_u32 addrs, struct Ip_sockaddr **sas) { int i; Ip_size_t offset = 0; for (i = 0; i < IPNET_RTAX_MAX; i++) { if (IP_BIT_ISSET(addrs, 1 << i)) { sas[i] = (struct Ip_sockaddr *) &((Ip_u8 *) msg)[offset]; offset += IPCOM_SA_LEN_GET(sas[i]); } else sas[i] = IP_NULL; } } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipmcp_create *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipmcp_create(void) { Ip_err ret; ipcom_memset(&ipmcp, 0, sizeof(Ipmcp_data_t)); if ((ret = ipcom_mutex_create(&ipmcp.daemon_mutex)) != IPCOM_SUCCESS) return ret; if ((ret = ipcom_sem_create(&ipmcp.cmd_sem, 0)) != IPCOM_SUCCESS) return ret; return IPCOM_SUCCESS; } /* *=========================================================================== * ipmcp_start *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipmcp_start(void) { Ip_err ret; Ipcom_proc_attr attr; ipcom_proc_attr_init(&attr); ipmcp.ipd_fd = IP_INVALID_SOCKET; ipmcp.rt_fd = IP_INVALID_SOCKET; #ifdef IPMCP_USE_IGMP ipmcp.igmp.fd = IP_INVALID_SOCKET; #endif #ifdef IPMCP_USE_MLD ipmcp.mld.mld_fd = IP_INVALID_SOCKET; ipmcp.mld.mrt_fd = IP_INVALID_SOCKET; #endif ret = ipcom_proc_acreate("ipmcp", ipmcp_daemon, &attr, IP_NULL); ip_assert(ret == IPCOM_SUCCESS); return ret; } /* *=========================================================================== * ipmcp_version *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC const char * ipmcp_version(void) { return "@(#) IPMCP $Name: VXWORKS_ITER40_2019102207 $ - INTERPEAK_COPYRIGHT_STRING"; } /* **************************************************************************** * END OF FILE **************************************************************************** */