/* * Copyright (c) 2013-2016 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include "config.h" #include #include #include "iwarp_pm.h" #include #include static const char iwpm_ulib_name [] = "iWarpPortMapperUser"; static __u16 iwpm_version = IWPM_UABI_VERSION; LIST_HEAD(mapping_reqs); /* list of map tracking objects */ LIST_HEAD(pending_messages); /* list of pending wire messages */ iwpm_client client_list[IWARP_PM_MAX_CLIENTS];/* list of iwarp port mapper clients */ static int mapinfo_num_list[IWARP_PM_MAX_CLIENTS]; /* list of iwarp port mapper clients */ /* socket handles */ static int pmv4_sock, pmv6_sock, netlink_sock, pmv4_client_sock, pmv6_client_sock; static pthread_t map_req_thread; /* handling mapping requests timeout */ pthread_cond_t cond_req_complete; pthread_mutex_t map_req_mutex = PTHREAD_MUTEX_INITIALIZER; int wake = 0; /* set if map_req_thread is wake */ static pthread_t pending_msg_thread; /* sending iwpm wire messages */ pthread_cond_t cond_pending_msg; pthread_mutex_t pending_msg_mutex = PTHREAD_MUTEX_INITIALIZER; static void iwpm_cleanup(void); static int print_mappings = 0; static int send_iwpm_mapinfo_request(int nl_sock, int client); /** * iwpm_signal_handler - Handle signals which iwarp port mapper receives * @signum: the number of the caught signal */ static void iwpm_signal_handler(int signum) { switch(signum) { case SIGHUP: syslog(LOG_WARNING, "iwpm_signal_handler: Received SIGHUP signal\n"); iwpm_cleanup(); exit(signum); break; case SIGTERM: syslog(LOG_WARNING, "iwpm_signal_handler: Received SIGTERM signal\n"); iwpm_cleanup(); exit(EXIT_SUCCESS); break; case SIGUSR1: syslog(LOG_WARNING, "iwpm_signal_handler: Received SIGUSR1 signal\n"); print_mappings = 1; break; default: syslog(LOG_WARNING, "iwpm_signal_handler: Unhandled signal %d\n", signum); break; } } /** * iwpm_mapping_reqs_handler - Handle mapping requests timeouts and retries */ static void *iwpm_mapping_reqs_handler(void *unused) { iwpm_mapping_request *iwpm_map_req, *next_map_req; int ret = 0; while (1) { pthread_mutex_lock(&map_req_mutex); wake = 0; if (list_empty(&mapping_reqs)) { /* wait until a new mapping request is posted */ ret = pthread_cond_wait(&cond_req_complete, &map_req_mutex); if (ret) { syslog(LOG_WARNING, "mapping_reqs_handler: " "Condition wait failed (ret = %d)\n", ret); pthread_mutex_unlock(&map_req_mutex); goto mapping_reqs_handler_exit; } } pthread_mutex_unlock(&map_req_mutex); /* update timeouts of the posted mapping requests */ do { pthread_mutex_lock(&map_req_mutex); wake = 1; list_for_each_safe(&mapping_reqs, iwpm_map_req, next_map_req, entry) { if (iwpm_map_req->timeout > 0) { if (iwpm_map_req->timeout < IWPM_MAP_REQ_TIMEOUT && iwpm_map_req->msg_type != IWARP_PM_REQ_ACK) { /* the request is still incomplete, retransmit the message (every 1sec) */ add_iwpm_pending_msg(iwpm_map_req->send_msg); iwpm_debug(IWARP_PM_RETRY_DBG, "mapping_reqs_handler: " "Going to retransmit a msg, map request " "(assochandle = %llu, type = %u, timeout = %d)\n", iwpm_map_req->assochandle, iwpm_map_req->msg_type, iwpm_map_req->timeout); } iwpm_map_req->timeout--; /* hang around for 10s */ } else { remove_iwpm_map_request(iwpm_map_req); } } pthread_mutex_unlock(&map_req_mutex); sleep(1); } while (!list_empty(&mapping_reqs)); } mapping_reqs_handler_exit: return NULL; } /** * iwpm_pending_msgs_handler - Handle sending iwarp port mapper wire messages */ static void *iwpm_pending_msgs_handler(void *unused) { iwpm_pending_msg *pending_msg; iwpm_send_msg *send_msg; int retries = IWPM_SEND_MSG_RETRIES; int ret = 0; pthread_mutex_lock(&pending_msg_mutex); while (1) { /* wait until a new message is posted */ ret = pthread_cond_wait(&cond_pending_msg, &pending_msg_mutex); if (ret) { syslog(LOG_WARNING, "pending_msgs_handler: " "Condition wait failed (ret = %d)\n", ret); pthread_mutex_unlock(&pending_msg_mutex); goto pending_msgs_handler_exit; } /* try sending out each pending message and remove it from the list */ while ((pending_msg = list_pop(&pending_messages, iwpm_pending_msg, entry))) { retries = IWPM_SEND_MSG_RETRIES; while (retries) { send_msg = &pending_msg->send_msg; /* send out the message */ int bytes_sent = sendto(send_msg->pm_sock, (char *)&send_msg->data, send_msg->length, 0, (struct sockaddr *)&send_msg->dest_addr, sizeof(send_msg->dest_addr)); if (bytes_sent != send_msg->length) { retries--; syslog(LOG_WARNING, "pending_msgs_handler: " "Could not send to PM Socket send_msg = %p, retries = %d\n", send_msg, retries); } else retries = 0; /* no need to retry */ } free(pending_msg); } } pthread_mutex_unlock(&pending_msg_mutex); pending_msgs_handler_exit: return NULL; } static int send_iwpm_error_msg(__u32, __u16, int, int); /* Register pid query - nlmsg attributes */ static struct nla_policy reg_pid_policy[IWPM_NLA_REG_PID_MAX] = { [IWPM_NLA_REG_PID_SEQ] = { .type = NLA_U32 }, [IWPM_NLA_REG_IF_NAME] = { .type = NLA_STRING, .maxlen = IWPM_IFNAME_SIZE }, [IWPM_NLA_REG_IBDEV_NAME] = { .type = NLA_STRING, .maxlen = IWPM_ULIBNAME_SIZE }, [IWPM_NLA_REG_ULIB_NAME] = { .type = NLA_STRING, .maxlen = IWPM_ULIBNAME_SIZE } }; /** * process_iwpm_register_pid - Service a client query for port mapper pid * @req_nlh: netlink header of the received client message * @client_idx: the index of the client (unique for each iwpm client) * @nl_sock: netlink socket to send a message back to the client * * Process a query and send a response to the client which contains the iwpm pid * nlmsg response attributes: * IWPM_NLA_RREG_PID_SEQ * IWPM_NLA_RREG_IBDEV_NAME * IWPM_NLA_RREG_ULIB_NAME * IWPM_NLA_RREG_ULIB_VER * IWPM_NLA_RREG_PID_ERR */ static int process_iwpm_register_pid(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { iwpm_client *client; struct nlattr *nltb [IWPM_NLA_REG_PID_MAX]; struct nl_msg *resp_nlmsg = NULL; const char *ifname, *devname, *libname; __u16 err_code = 0; const char *msg_type = "Register Pid Request"; const char *str_err; int ret = -EINVAL; if (parse_iwpm_nlmsg(req_nlh, IWPM_NLA_REG_PID_MAX, reg_pid_policy, nltb, msg_type)) { str_err = "Received Invalid nlmsg"; err_code = IWPM_INVALID_NLMSG_ERR; goto register_pid_error; } ifname = (const char *)nla_get_string(nltb[IWPM_NLA_REG_IF_NAME]); devname = (const char *)nla_get_string(nltb[IWPM_NLA_REG_IBDEV_NAME]); libname = (const char *)nla_get_string(nltb[IWPM_NLA_REG_ULIB_NAME]); iwpm_debug(IWARP_PM_NETLINK_DBG, "process_register_pid: PID request from " "IB device %s Ethernet device %s User library %s " "(client idx = %d, msg seq = %u).\n", devname, ifname, libname, client_idx, req_nlh->nlmsg_seq); /* register a first time client */ client = &client_list[client_idx]; if (!client->valid) { memcpy(client->ibdevname, devname, IWPM_DEVNAME_SIZE); memcpy(client->ifname, ifname, IWPM_IFNAME_SIZE); memcpy(client->ulibname, libname, IWPM_ULIBNAME_SIZE); client->valid = 1; } else { /* check client info */ if (strcmp(client->ulibname, libname)) { str_err = "Incorrect library version"; err_code = IWPM_USER_LIB_INFO_ERR; goto register_pid_error; } } resp_nlmsg = create_iwpm_nlmsg(req_nlh->nlmsg_type, client_idx); if (!resp_nlmsg) { ret = -ENOMEM; str_err = "Unable to create nlmsg response"; goto register_pid_error; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_RREG_PID_SEQ, req_nlh->nlmsg_seq))) goto register_pid_error; if ((ret = nla_put_string(resp_nlmsg, IWPM_NLA_RREG_IBDEV_NAME, devname))) goto register_pid_error; if ((ret = nla_put_string(resp_nlmsg, IWPM_NLA_RREG_ULIB_NAME, iwpm_ulib_name))) goto register_pid_error; if ((ret = nla_put_u16(resp_nlmsg, IWPM_NLA_RREG_ULIB_VER, iwpm_version))) goto register_pid_error; if ((ret = nla_put_u16(resp_nlmsg, IWPM_NLA_RREG_PID_ERR, err_code))) goto register_pid_error; if ((ret = send_iwpm_nlmsg(nl_sock, resp_nlmsg, req_nlh->nlmsg_pid))) { str_err = "Unable to send nlmsg response"; goto register_pid_error; } nlmsg_free(resp_nlmsg); return 0; register_pid_error: if (resp_nlmsg) nlmsg_free(resp_nlmsg); syslog(LOG_WARNING, "process_register_pid: %s ret = %d.\n", str_err, ret); if (err_code) send_iwpm_error_msg(req_nlh->nlmsg_seq, err_code, client_idx, nl_sock); return ret; } /* Add mapping request - nlmsg attributes */ static struct nla_policy manage_map_policy[IWPM_NLA_MANAGE_MAPPING_MAX] = { [IWPM_NLA_MANAGE_MAPPING_SEQ] = { .type = NLA_U32 }, [IWPM_NLA_MANAGE_ADDR] = { .minlen = sizeof(struct sockaddr_storage) }, [IWPM_NLA_MANAGE_FLAGS] = { .type = NLA_U32 } }; /** * process_iwpm_add_mapping - Service a client request for mapping of a local address * @req_nlh: netlink header of the received client message * @client_idx: the index of the client (unique for each iwpm client) * @nl_sock: netlink socket to send a message back to the client * * Process a mapping request for a local address and send a response to the client * which contains the mapped local address (IP address and TCP port) * nlmsg response attributes: * [IWPM_NLA_RMANAGE_MAPPING_SEQ] * [IWPM_NLA_RMANAGE_ADDR] * [IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR] * [IWPM_NLA_RMANAGE_MAPPING_ERR] */ static int process_iwpm_add_mapping(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { iwpm_mapped_port *iwpm_port = NULL; struct nlattr *nltb [IWPM_NLA_MANAGE_MAPPING_MAX] = {}; struct nl_msg *resp_nlmsg = NULL; struct sockaddr_storage *local_addr; int not_mapped = 1; __u16 err_code = 0; const char *msg_type = "Add Mapping Request"; const char *str_err = ""; int ret = -EINVAL; __u32 flags; int max = IWPM_NLA_MANAGE_MAPPING_MAX; if (iwpm_version != IWPM_UABI_VERSION) max--; if (parse_iwpm_nlmsg(req_nlh, max, manage_map_policy, nltb, msg_type)) { err_code = IWPM_INVALID_NLMSG_ERR; str_err = "Received Invalid nlmsg"; goto add_mapping_error; } local_addr = (struct sockaddr_storage *)nla_data(nltb[IWPM_NLA_MANAGE_ADDR]); flags = nltb[IWPM_NLA_MANAGE_FLAGS] ? nla_get_u32(nltb[IWPM_NLA_MANAGE_FLAGS]) : 0; iwpm_port = find_iwpm_mapping(local_addr, not_mapped); if (iwpm_port) { if (check_same_sockaddr(local_addr, &iwpm_port->local_addr) && iwpm_port->wcard) { atomic_fetch_add(&iwpm_port->ref_cnt, 1); } else { err_code = IWPM_DUPLICATE_MAPPING_ERR; str_err = "Duplicate mapped port"; goto add_mapping_error; } } else { iwpm_port = create_iwpm_mapped_port(local_addr, client_idx, flags); if (!iwpm_port) { err_code = IWPM_CREATE_MAPPING_ERR; str_err = "Unable to create new mapping"; goto add_mapping_error; } } resp_nlmsg = create_iwpm_nlmsg(req_nlh->nlmsg_type, client_idx); if (!resp_nlmsg) { ret = -ENOMEM; str_err = "Unable to create nlmsg response"; goto add_mapping_free_error; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_RMANAGE_MAPPING_SEQ, req_nlh->nlmsg_seq))) goto add_mapping_free_error; if ((ret = nla_put(resp_nlmsg, IWPM_NLA_RMANAGE_ADDR, sizeof(struct sockaddr_storage), &iwpm_port->local_addr))) goto add_mapping_free_error; if ((ret = nla_put(resp_nlmsg, IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR, sizeof(struct sockaddr_storage), &iwpm_port->mapped_addr))) goto add_mapping_free_error; if ((ret = nla_put_u16(resp_nlmsg, IWPM_NLA_RMANAGE_MAPPING_ERR, err_code))) goto add_mapping_free_error; if ((ret = send_iwpm_nlmsg(nl_sock, resp_nlmsg, req_nlh->nlmsg_pid))) { str_err = "Unable to send nlmsg response"; goto add_mapping_free_error; } /* add the new mapping to the list */ add_iwpm_mapped_port(iwpm_port); nlmsg_free(resp_nlmsg); return 0; add_mapping_free_error: if (resp_nlmsg) nlmsg_free(resp_nlmsg); if (iwpm_port) { if (atomic_fetch_sub(&iwpm_port->ref_cnt, 1) == 1) free_iwpm_port(iwpm_port); } add_mapping_error: syslog(LOG_WARNING, "process_add_mapping: %s (failed request from client = %s).\n", str_err, client_list[client_idx].ibdevname); if (err_code) { /* send error message to the client */ send_iwpm_error_msg(req_nlh->nlmsg_seq, err_code, client_idx, nl_sock); } return ret; } /* Query mapping request - nlmsg attributes */ static struct nla_policy query_map_policy[IWPM_NLA_QUERY_MAPPING_MAX] = { [IWPM_NLA_QUERY_MAPPING_SEQ] = { .type = NLA_U32 }, [IWPM_NLA_QUERY_LOCAL_ADDR] = { .minlen = sizeof(struct sockaddr_storage) }, [IWPM_NLA_QUERY_REMOTE_ADDR] = { .minlen = sizeof(struct sockaddr_storage) }, [IWPM_NLA_QUERY_FLAGS] = { .type = NLA_U32 } }; /** * process_iwpm_query_mapping - Service a client request for local and remote mapping * @req_nlh: netlink header of the received client message * @client_idx: the index of the client (the index is unique for each iwpm client) * @nl_sock: netlink socket to send a message back to the client * * Process a client request for local and remote address mapping * Create mapping for the local address (IP address and TCP port) * Send a request to the remote port mapper peer to find out the remote address mapping */ static int process_iwpm_query_mapping(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { iwpm_mapped_port *iwpm_port = NULL; iwpm_mapping_request *iwpm_map_req = NULL; struct nlattr *nltb [IWPM_NLA_QUERY_MAPPING_MAX] = {}; struct sockaddr_storage *local_addr, *remote_addr; sockaddr_union dest_addr; iwpm_msg_parms msg_parms; iwpm_send_msg *send_msg = NULL; int pm_client_sock; int not_mapped = 1; __u16 err_code = 0; const char *msg_type = "Add & Query Mapping Request"; const char *str_err = ""; int ret = -EINVAL; __u32 flags; int max = IWPM_NLA_QUERY_MAPPING_MAX; if (iwpm_version != IWPM_UABI_VERSION) max--; if (parse_iwpm_nlmsg(req_nlh, max, query_map_policy, nltb, msg_type)) { err_code = IWPM_INVALID_NLMSG_ERR; str_err = "Received Invalid nlmsg"; goto query_mapping_error; } local_addr = (struct sockaddr_storage *)nla_data(nltb[IWPM_NLA_QUERY_LOCAL_ADDR]); remote_addr = (struct sockaddr_storage *)nla_data(nltb[IWPM_NLA_QUERY_REMOTE_ADDR]); flags = nltb[IWPM_NLA_QUERY_FLAGS] ? nla_get_u32(nltb[IWPM_NLA_QUERY_FLAGS]) : 0; iwpm_port = find_iwpm_mapping(local_addr, not_mapped); if (iwpm_port) { atomic_fetch_add(&iwpm_port->ref_cnt, 1); } else { iwpm_port = create_iwpm_mapped_port(local_addr, client_idx, flags); if (!iwpm_port) { err_code = IWPM_CREATE_MAPPING_ERR; str_err = "Unable to create new mapping"; goto query_mapping_error; } } if (iwpm_port->wcard) { err_code = IWPM_CREATE_MAPPING_ERR; str_err = "Invalid wild card mapping"; goto query_mapping_free_error; } /* create iwpm wire message */ memcpy(&dest_addr.s_sockaddr, remote_addr, sizeof(struct sockaddr_storage)); switch (dest_addr.s_sockaddr.ss_family) { case AF_INET: dest_addr.v4_sockaddr.sin_port = htobe16(IWARP_PM_PORT); msg_parms.ip_ver = 4; msg_parms.address_family = AF_INET; pm_client_sock = pmv4_client_sock; break; case AF_INET6: dest_addr.v6_sockaddr.sin6_port = htobe16(IWARP_PM_PORT); msg_parms.ip_ver = 6; msg_parms.address_family = AF_INET6; pm_client_sock = pmv6_client_sock; break; default: str_err = "Invalid Internet address family"; goto query_mapping_free_error; } /* fill in the remote peer address and the local mapped address */ copy_iwpm_sockaddr(dest_addr.s_sockaddr.ss_family, remote_addr, NULL, NULL, &msg_parms.apipaddr[0], &msg_parms.apport); copy_iwpm_sockaddr(dest_addr.s_sockaddr.ss_family, local_addr, NULL, NULL, &msg_parms.cpipaddr[0], &msg_parms.cpport); copy_iwpm_sockaddr(dest_addr.s_sockaddr.ss_family, &iwpm_port->mapped_addr, NULL, NULL, &msg_parms.mapped_cpipaddr[0], &msg_parms.mapped_cpport); msg_parms.pmtime = 0; msg_parms.ver = 0; iwpm_debug(IWARP_PM_WIRE_DBG, "process_query_mapping: Local port = 0x%04X, " "remote port = 0x%04X\n", be16toh(msg_parms.cpport), be16toh(msg_parms.apport)); ret = -ENOMEM; send_msg = malloc(sizeof(iwpm_send_msg)); if (!send_msg) { str_err = "Unable to allocate send msg buffer"; goto query_mapping_free_error; } iwpm_map_req = create_iwpm_map_request(req_nlh, &iwpm_port->local_addr, remote_addr, 0, IWARP_PM_REQ_QUERY, send_msg); if (!iwpm_map_req) { str_err = "Unable to allocate mapping request"; goto query_mapping_free_error; } msg_parms.assochandle = iwpm_map_req->assochandle; form_iwpm_request(&send_msg->data, &msg_parms); form_iwpm_send_msg(pm_client_sock, &dest_addr.s_sockaddr, msg_parms.msize, send_msg); add_iwpm_map_request(iwpm_map_req); add_iwpm_mapped_port(iwpm_port); return send_iwpm_msg(form_iwpm_request, &msg_parms, &dest_addr.s_sockaddr, pm_client_sock); query_mapping_free_error: if (iwpm_port) { if (atomic_fetch_sub(&iwpm_port->ref_cnt, 1) == 1) free_iwpm_port(iwpm_port); } if (send_msg) free(send_msg); if (iwpm_map_req) free(iwpm_map_req); query_mapping_error: syslog(LOG_WARNING, "process_query_mapping: %s (failed request from client = %s).\n", str_err, client_list[client_idx].ibdevname); if (err_code) { /* send error message to the client */ send_iwpm_error_msg(req_nlh->nlmsg_seq, err_code, client_idx, nl_sock); } return ret; } /** * process_iwpm_remove_mapping - Remove a local mapping and close the mapped TCP port * @req_nlh: netlink header of the received client message * @client_idx: the index of the client (the index is unique for each iwpm client) * @nl_sock: netlink socket to send a message to the client */ static int process_iwpm_remove_mapping(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { iwpm_mapped_port *iwpm_port = NULL; struct sockaddr_storage *local_addr; struct nlattr *nltb [IWPM_NLA_MANAGE_MAPPING_MAX]; int not_mapped = 1; const char *msg_type = "Remove Mapping Request"; int ret = 0; if (parse_iwpm_nlmsg(req_nlh, IWPM_NLA_REMOVE_MAPPING_MAX, manage_map_policy, nltb, msg_type)) { send_iwpm_error_msg(req_nlh->nlmsg_seq, IWPM_INVALID_NLMSG_ERR, client_idx, nl_sock); syslog(LOG_WARNING, "process_remove_mapping: Received Invalid nlmsg from client = %d\n", client_idx); ret = -EINVAL; goto remove_mapping_exit; } local_addr = (struct sockaddr_storage *)nla_data(nltb[IWPM_NLA_MANAGE_ADDR]); iwpm_debug(IWARP_PM_NETLINK_DBG, "process_remove_mapping: Going to remove mapping" " (client idx = %d)\n", client_idx); iwpm_port = find_iwpm_same_mapping(local_addr, not_mapped); if (!iwpm_port) { iwpm_debug(IWARP_PM_NETLINK_DBG, "process_remove_mapping: Unable to find mapped port object\n"); print_iwpm_sockaddr(local_addr, "process_remove_mapping: Local address", IWARP_PM_ALL_DBG); /* the client sends a remove mapping request when terminating a connection and it is possible that there isn't a successful mapping for this connection */ goto remove_mapping_exit; } if (iwpm_port->owner_client != client_idx) { syslog(LOG_WARNING, "process_remove_mapping: Invalid request from client = %d\n", client_idx); goto remove_mapping_exit; } if (atomic_fetch_sub(&iwpm_port->ref_cnt, 1) == 1) { remove_iwpm_mapped_port(iwpm_port); free_iwpm_port(iwpm_port); } remove_mapping_exit: return ret; } static int send_conn_info_nlmsg(struct sockaddr_storage *local_addr, struct sockaddr_storage *remote_addr, struct sockaddr_storage *mapped_loc_addr, struct sockaddr_storage *mapped_rem_addr, int owner_client, __u16 nlmsg_type, __u32 nlmsg_seq, __u32 nlmsg_pid, __u16 nlmsg_err, int nl_sock) { struct nl_msg *resp_nlmsg = NULL; const char *str_err; int ret; resp_nlmsg = create_iwpm_nlmsg(nlmsg_type, owner_client); if (!resp_nlmsg) { str_err = "Unable to create nlmsg response"; ret = -ENOMEM; goto nlmsg_error; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_QUERY_MAPPING_SEQ, nlmsg_seq))) goto nlmsg_free_error; if ((ret = nla_put(resp_nlmsg, IWPM_NLA_QUERY_LOCAL_ADDR, sizeof(struct sockaddr_storage), local_addr))) goto nlmsg_free_error; if ((ret = nla_put(resp_nlmsg, IWPM_NLA_QUERY_REMOTE_ADDR, sizeof(struct sockaddr_storage), remote_addr))) goto nlmsg_free_error; if ((ret = nla_put(resp_nlmsg, IWPM_NLA_RQUERY_MAPPED_LOC_ADDR, sizeof(struct sockaddr_storage), mapped_loc_addr))) goto nlmsg_free_error; if ((ret = nla_put(resp_nlmsg, IWPM_NLA_RQUERY_MAPPED_REM_ADDR, sizeof(struct sockaddr_storage), mapped_rem_addr))) goto nlmsg_free_error; if ((ret = nla_put_u16(resp_nlmsg, IWPM_NLA_RQUERY_MAPPING_ERR, nlmsg_err))) goto nlmsg_free_error; if ((ret = send_iwpm_nlmsg(nl_sock, resp_nlmsg, nlmsg_pid))) { str_err = "Unable to send nlmsg response"; goto nlmsg_free_error; } nlmsg_free(resp_nlmsg); return 0; nlmsg_free_error: if (resp_nlmsg) nlmsg_free(resp_nlmsg); nlmsg_error: syslog(LOG_WARNING, "send_conn_info_nlmsg: %s.\n", str_err); return ret; } /** * process_iwpm_wire_request - Process a mapping query from remote port mapper peer * @msg_parms: the received iwpm request message * @recv_addr: address of the remote peer * @pm_sock: socket handle to send a response to the remote iwpm peer * * Look up the accepting peer local address to find the corresponding mapping, * send reject message to the remote connecting peer, if no mapping is found, * otherwise, send accept message with the accepting peer mapping info */ static int process_iwpm_wire_request(iwpm_msg_parms *msg_parms, int nl_sock, struct sockaddr_storage *recv_addr, int pm_sock) { iwpm_mapped_port *iwpm_port; iwpm_mapping_request *iwpm_map_req = NULL; iwpm_mapping_request iwpm_copy_req; iwpm_send_msg *send_msg = NULL; struct sockaddr_storage local_addr, mapped_loc_addr; struct sockaddr_storage remote_addr = {}, mapped_rem_addr = {}; __u16 nlmsg_type; int not_mapped = 1; int ret = 0; copy_iwpm_sockaddr(msg_parms->address_family, NULL, &local_addr, &msg_parms->apipaddr[0], NULL, &msg_parms->apport); iwpm_port = find_iwpm_mapping(&local_addr, not_mapped); if (!iwpm_port) { /* could not find mapping for the requested address */ iwpm_debug(IWARP_PM_WIRE_DBG, "process_wire_request: " "Sending Reject to port mapper peer.\n"); print_iwpm_sockaddr(&local_addr, "process_wire_request: Local address", IWARP_PM_ALL_DBG); return send_iwpm_msg(form_iwpm_reject, msg_parms, recv_addr, pm_sock); } /* record mapping in the accept message */ if (iwpm_port->wcard) msg_parms->apport = get_sockaddr_port(&iwpm_port->mapped_addr); else copy_iwpm_sockaddr(msg_parms->address_family, &iwpm_port->mapped_addr, NULL, NULL, &msg_parms->apipaddr[0], &msg_parms->apport); copy_iwpm_sockaddr(msg_parms->address_family, NULL, &mapped_loc_addr, &msg_parms->apipaddr[0], NULL, &msg_parms->apport); /* check if there is already a request */ ret = update_iwpm_map_request(msg_parms->assochandle, &mapped_loc_addr, IWARP_PM_REQ_ACCEPT, &iwpm_copy_req, 0); if (!ret) { /* found request */ iwpm_debug(IWARP_PM_WIRE_DBG,"process_wire_request: Detected retransmission " "map request (assochandle = %llu type = %d timeout = %u complete = %d)\n", iwpm_copy_req.assochandle, iwpm_copy_req.msg_type, iwpm_copy_req.timeout, iwpm_copy_req.complete); return 0; } /* allocate response message */ send_msg = malloc(sizeof(iwpm_send_msg)); if (!send_msg) { syslog(LOG_WARNING, "process_wire_request: Unable to allocate send msg.\n"); return -ENOMEM; } form_iwpm_accept(&send_msg->data, msg_parms); form_iwpm_send_msg(pm_sock, recv_addr, msg_parms->msize, send_msg); copy_iwpm_sockaddr(msg_parms->address_family, NULL, &remote_addr, &msg_parms->cpipaddr[0], NULL, &msg_parms->cpport); copy_iwpm_sockaddr(msg_parms->address_family, NULL, &mapped_rem_addr, &msg_parms->mapped_cpipaddr[0], NULL, &msg_parms->mapped_cpport); iwpm_map_req = create_iwpm_map_request(NULL, &mapped_loc_addr, &remote_addr, msg_parms->assochandle, IWARP_PM_REQ_ACCEPT, send_msg); if (!iwpm_map_req) { syslog(LOG_WARNING, "process_wire_request: Unable to allocate mapping request.\n"); free(send_msg); return -ENOMEM; } add_iwpm_map_request(iwpm_map_req); ret = send_iwpm_msg(form_iwpm_accept, msg_parms, recv_addr, pm_sock); if (ret) { syslog(LOG_WARNING, "process_wire_request: Unable to allocate accept message.\n"); return ret; } nlmsg_type = RDMA_NL_GET_TYPE(iwpm_port->owner_client, RDMA_NL_IWPM_REMOTE_INFO); ret = send_conn_info_nlmsg(&iwpm_port->local_addr, &remote_addr, &iwpm_port->mapped_addr, &mapped_rem_addr, iwpm_port->owner_client, nlmsg_type, 0, 0, 0, nl_sock); return ret; } /** * process_iwpm_wire_accept - Process accept message from the remote port mapper peer * @msg_parms: the received iwpm accept message, containing the remote peer mapping info * @nl_sock: netlink socket to send a message to the iwpm client * @recv_addr: address of the remote peer * @pm_sock: socket handle to send ack message back to the remote peer * * Send acknowledgement to the remote/accepting peer, * send a netlink message with the local and remote mapping info to the iwpm client * nlmsg response attributes: * [IWPM_NLA_QUERY_MAPPING_SEQ] * [IWPM_NLA_QUERY_LOCAL_ADDR] * [IWPM_NLA_QUERY_REMOTE_ADDR] * [IWPM_NLA_RQUERY_MAPPED_LOC_ADDR] * [IWPM_NLA_RQUERY_MAPPED_REM_ADDR] * [IWPM_NLA_RQUERY_MAPPING_ERR] */ static int process_iwpm_wire_accept(iwpm_msg_parms *msg_parms, int nl_sock, struct sockaddr_storage *recv_addr, int pm_sock) { iwpm_mapping_request iwpm_map_req; iwpm_mapping_request *iwpm_retry_req = NULL; iwpm_mapped_port *iwpm_port; struct sockaddr_storage local_addr, remote_mapped_addr; int not_mapped = 1; const char *str_err; int ret; copy_iwpm_sockaddr(msg_parms->address_family, NULL, &local_addr, &msg_parms->cpipaddr[0], NULL, &msg_parms->cpport); copy_iwpm_sockaddr(msg_parms->address_family, NULL, &remote_mapped_addr, &msg_parms->apipaddr[0], NULL, &msg_parms->apport); ret = -EINVAL; iwpm_port = find_iwpm_same_mapping(&local_addr, not_mapped); if (!iwpm_port) { iwpm_debug(IWARP_PM_WIRE_DBG, "process_wire_accept: " "Received accept for unknown mapping.\n"); return 0; } /* there should be a request for the accept message */ ret = update_iwpm_map_request(msg_parms->assochandle, &iwpm_port->local_addr, (IWARP_PM_REQ_QUERY|IWARP_PM_REQ_ACK), &iwpm_map_req, 1); if (ret) { iwpm_debug(IWARP_PM_WIRE_DBG, "process_wire_accept: " "No matching mapping request (assochandle = %llu)\n", msg_parms->assochandle); return 0; /* ok when retransmission */ } if (iwpm_map_req.complete) return 0; /* if the accept has already been processed and this is retransmission */ if (iwpm_map_req.msg_type == IWARP_PM_REQ_ACK) { iwpm_debug(IWARP_PM_RETRY_DBG, "process_wire_accept: Detected retransmission " "(map request assochandle = %llu)\n", iwpm_map_req.assochandle); goto wire_accept_send_ack; } ret = send_conn_info_nlmsg(&iwpm_port->local_addr, &iwpm_map_req.remote_addr, &iwpm_port->mapped_addr, &remote_mapped_addr, iwpm_port->owner_client, iwpm_map_req.nlmsg_type, iwpm_map_req.nlmsg_seq, iwpm_map_req.nlmsg_pid, 0, nl_sock); if (ret) { str_err = "Unable to send nlmsg response"; goto wire_accept_error; } /* object to detect retransmission */ iwpm_retry_req = create_iwpm_map_request(NULL, &iwpm_map_req.src_addr, &iwpm_map_req.remote_addr, iwpm_map_req.assochandle, IWARP_PM_REQ_ACK, NULL); if (!iwpm_retry_req) { ret = -ENOMEM; str_err = "Unable to allocate retry request"; goto wire_accept_error; } add_iwpm_map_request(iwpm_retry_req); wire_accept_send_ack: return send_iwpm_msg(form_iwpm_ack, msg_parms, recv_addr, pm_sock); wire_accept_error: syslog(LOG_WARNING, "process_iwpm_wire_accept: %s.\n", str_err); return ret; } /** * process_iwpm_wire_reject - Process reject message from the port mapper remote peer * @msg_parms: the received iwpm reject message * @nl_sock: netlink socket to send through a message to the iwpm client * * Send notification to the iwpm client that its * mapping request is rejected by the remote/accepting port mapper peer */ static int process_iwpm_wire_reject(iwpm_msg_parms *msg_parms, int nl_sock) { iwpm_mapping_request iwpm_map_req; iwpm_mapped_port *iwpm_port; struct sockaddr_storage local_addr, remote_addr; int not_mapped = 1; __u16 err_code = IWPM_REMOTE_QUERY_REJECT; const char *str_err; int ret = -EINVAL; copy_iwpm_sockaddr(msg_parms->address_family, NULL, &local_addr, &msg_parms->cpipaddr[0], NULL, &msg_parms->cpport); copy_iwpm_sockaddr(msg_parms->address_family, NULL, &remote_addr, &msg_parms->apipaddr[0], NULL, &msg_parms->apport); print_iwpm_sockaddr(&local_addr, "process_wire_reject: Local address", IWARP_PM_ALL_DBG); print_iwpm_sockaddr(&remote_addr, "process_wire_reject: Remote address", IWARP_PM_ALL_DBG); ret = -EINVAL; iwpm_port = find_iwpm_same_mapping(&local_addr, not_mapped); if (!iwpm_port) { syslog(LOG_WARNING, "process_wire_reject: Received reject for unknown mapping.\n"); return 0; } /* make sure there is request posted */ ret = update_iwpm_map_request(msg_parms->assochandle, &iwpm_port->local_addr, IWARP_PM_REQ_QUERY, &iwpm_map_req, 1); if (ret) { iwpm_debug(IWARP_PM_WIRE_DBG, "process_wire_reject: " "No matching mapping request (assochandle = %llu)\n", msg_parms->assochandle); return 0; /* ok when retransmission */ } if (iwpm_map_req.complete) return 0; ret = send_conn_info_nlmsg(&iwpm_port->local_addr, &iwpm_map_req.remote_addr, &iwpm_port->mapped_addr, &iwpm_map_req.remote_addr, iwpm_port->owner_client, iwpm_map_req.nlmsg_type, iwpm_map_req.nlmsg_seq, iwpm_map_req.nlmsg_pid, err_code, nl_sock); if (ret) { str_err = "Unable to send nlmsg response"; goto wire_reject_error; } return 0; wire_reject_error: syslog(LOG_WARNING, "process_wire_reject: %s.\n", str_err); return ret; } /** * process_iwpm_wire_ack - Process acknowledgement from the remote port mapper peer * @msg_parms: received iwpm acknowledgement */ static int process_iwpm_wire_ack(iwpm_msg_parms *msg_parms) { iwpm_mapped_port *iwpm_port; iwpm_mapping_request iwpm_map_req; struct sockaddr_storage local_mapped_addr; int not_mapped = 0; int ret; copy_iwpm_sockaddr(msg_parms->address_family, NULL, &local_mapped_addr, &msg_parms->apipaddr[0], NULL, &msg_parms->apport); iwpm_port = find_iwpm_mapping(&local_mapped_addr, not_mapped); if (!iwpm_port) { iwpm_debug(IWARP_PM_WIRE_DBG, "process_wire_ack: Received ack for unknown mapping.\n"); return 0; } /* make sure there is accept for the ack */ ret = update_iwpm_map_request(msg_parms->assochandle, &local_mapped_addr, IWARP_PM_REQ_ACCEPT, &iwpm_map_req, 1); if (ret) iwpm_debug(IWARP_PM_WIRE_DBG, "process_wire_ack: No matching mapping request\n"); return 0; } /* Mapping info message - nlmsg attributes */ static struct nla_policy mapinfo_policy[IWPM_NLA_MAPINFO_MAX] = { [IWPM_NLA_MAPINFO_LOCAL_ADDR] = { .minlen = sizeof(struct sockaddr_storage) }, [IWPM_NLA_MAPINFO_MAPPED_ADDR] = { .minlen = sizeof(struct sockaddr_storage) }, [IWPM_NLA_MAPINFO_FLAGS] = { .type = NLA_U32 } }; /** * process_iwpm_mapinfo - Process a mapping info message from the port mapper client * @req_nlh: netlink header of the received client message * @client_idx: the index of the client (the index is unique for each iwpm client) * @nl_sock: netlink socket to send a message to the client * * In case the userspace iwarp port mapper daemon is restarted, * the iwpm client needs to send a record of mappings it is currently using. * The port mapper needs to reopen the mapped ports used by the client. */ static int process_iwpm_mapinfo(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { iwpm_mapped_port *iwpm_port = NULL; struct sockaddr_storage *local_addr, *local_mapped_addr; struct nlattr *nltb [IWPM_NLA_MAPINFO_MAX] = {}; int not_mapped = 1; __u16 err_code = 0; const char *msg_type = "Mapping Info Msg"; const char *str_err = ""; int ret = -EINVAL; __u32 flags; int max = IWPM_NLA_MAPINFO_MAX; if (iwpm_version != IWPM_UABI_VERSION) max--; if (parse_iwpm_nlmsg(req_nlh, max, mapinfo_policy, nltb, msg_type)) { err_code = IWPM_INVALID_NLMSG_ERR; str_err = "Received Invalid nlmsg"; goto process_mapinfo_error; } local_addr = (struct sockaddr_storage *)nla_data(nltb[IWPM_NLA_MAPINFO_LOCAL_ADDR]); local_mapped_addr = (struct sockaddr_storage *)nla_data(nltb[IWPM_NLA_MAPINFO_MAPPED_ADDR]); flags = nltb[IWPM_NLA_MAPINFO_FLAGS] ? nla_get_u32(nltb[IWPM_NLA_MAPINFO_FLAGS]) : 0; iwpm_port = find_iwpm_mapping(local_addr, not_mapped); if (iwpm_port) { /* Can be safely ignored, if the mapinfo is exactly the same, * because the client will provide all the port information it has and * it could have started using the port mapper service already */ if (check_same_sockaddr(&iwpm_port->local_addr, local_addr) && check_same_sockaddr(&iwpm_port->mapped_addr, local_mapped_addr)) goto process_mapinfo_exit; /* partial duplicates matching wcard ip address aren't allowed as well */ err_code = IWPM_DUPLICATE_MAPPING_ERR; str_err = "Duplicate mapped port"; goto process_mapinfo_error; } iwpm_port = reopen_iwpm_mapped_port(local_addr, local_mapped_addr, client_idx, flags); if (!iwpm_port) { err_code = IWPM_CREATE_MAPPING_ERR; str_err = "Unable to create new mapping"; goto process_mapinfo_error; } /* add the new mapping to the list */ add_iwpm_mapped_port(iwpm_port); process_mapinfo_exit: mapinfo_num_list[client_idx]++; return 0; process_mapinfo_error: syslog(LOG_WARNING, "process_mapinfo: %s.\n", str_err); if (err_code) { /* send error message to the client */ send_iwpm_error_msg(req_nlh->nlmsg_seq, err_code, client_idx, nl_sock); } return ret; } /* Mapping info message count - nlmsg attributes */ static struct nla_policy mapinfo_count_policy[IWPM_NLA_MAPINFO_SEND_MAX] = { [IWPM_NLA_MAPINFO_SEQ] = { .type = NLA_U32 }, [IWPM_NLA_MAPINFO_SEND_NUM] = { .type = NLA_U32 } }; /** * process_iwpm_mapinfo_count - Process mapinfo count message * @req_nlh: netlink header of the received message from the client * @client_idx: the index of the client * @nl_sock: netlink socket to send a message to the client * * Mapinfo count message is a mechanism for the port mapper and the client to * synchronize on the number of mapinfo messages which were sucessfully exchanged and processed */ static int process_iwpm_mapinfo_count(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { struct nlattr *nltb [IWPM_NLA_MAPINFO_SEND_MAX]; struct nl_msg *resp_nlmsg = NULL; const char *msg_type = "Number of Mappings Msg"; __u32 map_count; __u16 err_code = 0; const char *str_err = ""; int ret = -EINVAL; if (parse_iwpm_nlmsg(req_nlh, IWPM_NLA_MAPINFO_SEND_MAX, mapinfo_count_policy, nltb, msg_type)) { str_err = "Received Invalid nlmsg"; err_code = IWPM_INVALID_NLMSG_ERR; goto mapinfo_count_error; } map_count = nla_get_u32(nltb[IWPM_NLA_MAPINFO_SEND_NUM]); if (map_count != mapinfo_num_list[client_idx]) iwpm_debug(IWARP_PM_NETLINK_DBG, "get_mapinfo_count: Client (idx = %d) " "send mapinfo count = %u processed mapinfo count = %u.\n", client_idx, map_count, mapinfo_num_list[client_idx]); resp_nlmsg = create_iwpm_nlmsg(req_nlh->nlmsg_type, client_idx); if (!resp_nlmsg) { str_err = "Unable to create nlmsg response"; ret = -ENOMEM; goto mapinfo_count_error; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_MAPINFO_SEQ, req_nlh->nlmsg_seq))) goto mapinfo_count_free_error; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_MAPINFO_SEND_NUM, map_count))) goto mapinfo_count_free_error; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_MAPINFO_ACK_NUM, mapinfo_num_list[client_idx]))) goto mapinfo_count_free_error; if ((ret = send_iwpm_nlmsg(nl_sock, resp_nlmsg, req_nlh->nlmsg_pid))) { str_err = "Unable to send nlmsg response"; goto mapinfo_count_free_error; } nlmsg_free(resp_nlmsg); return 0; mapinfo_count_free_error: if (resp_nlmsg) nlmsg_free(resp_nlmsg); mapinfo_count_error: syslog(LOG_WARNING, "process_mapinfo_count: %s.\n", str_err); if (err_code) { /* send error message to the client */ send_iwpm_error_msg(req_nlh->nlmsg_seq, err_code, client_idx, nl_sock); } return ret; } /** * send_iwpm_error_msg - Send error message to the iwpm client * @seq: last received netlink message sequence * @err_code: used to differentiante between errors * @client_idx: the index of the client * @nl_sock: netlink socket to send a message to the client */ static int send_iwpm_error_msg(__u32 seq, __u16 err_code, int client_idx, int nl_sock) { struct nl_msg *resp_nlmsg; __u16 nlmsg_type; const char *str_err = ""; int ret; nlmsg_type = RDMA_NL_GET_TYPE(client_idx, RDMA_NL_IWPM_HANDLE_ERR); resp_nlmsg = create_iwpm_nlmsg(nlmsg_type, client_idx); if (!resp_nlmsg) { ret = -ENOMEM; str_err = "Unable to create nlmsg response"; goto send_error_msg_exit; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_u32(resp_nlmsg, IWPM_NLA_ERR_SEQ, seq))) goto send_error_msg_exit; if ((ret = nla_put_u16(resp_nlmsg, IWPM_NLA_ERR_CODE, err_code))) goto send_error_msg_exit; if ((ret = send_iwpm_nlmsg(nl_sock, resp_nlmsg, 0))) { str_err = "Unable to send nlmsg response"; goto send_error_msg_exit; } nlmsg_free(resp_nlmsg); return 0; send_error_msg_exit: if (resp_nlmsg) nlmsg_free(resp_nlmsg); syslog(LOG_WARNING, "send_iwpm_error_msg: %s (ret = %d).\n", str_err, ret); return ret; } /* Hello message - nlmsg attributes */ static struct nla_policy hello_policy[IWPM_NLA_HELLO_MAX] = { [IWPM_NLA_HELLO_ABI_VERSION] = { .type = NLA_U16 } }; /** * process_iwpm_hello - Process mapinfo count message * @req_nlh: netlink header of the received message from the client * @client_idx: the index of the client * @nl_sock: netlink socket to send a message to the client * * Mapinfo count message is a mechanism for the port mapper and the client to * synchronize on the number of mapinfo messages which were sucessfully exchanged and processed */ static int process_iwpm_hello(struct nlmsghdr *req_nlh, int client_idx, int nl_sock) { struct nlattr *nltb [IWPM_NLA_HELLO_MAX]; const char *msg_type = "Hello Msg"; __u16 abi_version; __u16 err_code = 0; const char *str_err = ""; int ret = -EINVAL; if (req_nlh->nlmsg_type == NLMSG_ERROR) { abi_version = IWPM_UABI_VERSION_MIN; } else { if (parse_iwpm_nlmsg(req_nlh, IWPM_NLA_HELLO_MAX, hello_policy, nltb, msg_type)) { str_err = "Received Invalid nlmsg"; err_code = IWPM_INVALID_NLMSG_ERR; goto hello_error; } abi_version = nla_get_u16(nltb[IWPM_NLA_HELLO_ABI_VERSION]); } if (abi_version > IWPM_UABI_VERSION) { str_err = "UABI Version mismatch"; err_code = IWPM_VERSION_MISMATCH_ERR; goto hello_error; } iwpm_version = abi_version; iwpm_debug(IWARP_PM_NETLINK_DBG, "process_iwpm_hello: using abi_version %u\n", iwpm_version); send_iwpm_mapinfo_request(nl_sock, RDMA_NL_IWCM); if (iwpm_version == 3) { /* Legacy RDMA_NL_C4IW for old kernels */ send_iwpm_mapinfo_request(nl_sock, RDMA_NL_IWCM+1); } return 0; hello_error: syslog(LOG_WARNING, "process_iwpm_hello: %s.\n", str_err); if (err_code) { /* send error message to the client */ send_iwpm_error_msg(req_nlh->nlmsg_seq, err_code, client_idx, nl_sock); } return ret; } /** * process_iwpm_netlink_msg - Dispatch received netlink messages * @nl_sock: netlink socket to read the messages from */ static int process_iwpm_netlink_msg(int nl_sock) { char *recv_buffer = NULL; struct nlmsghdr *nlh; struct sockaddr_nl src_addr; int len, type, client_idx, op; socklen_t src_addr_len; const char *str_err = ""; int ret = 0; recv_buffer = malloc(NLMSG_SPACE(IWARP_PM_RECV_PAYLOAD)); if (!recv_buffer) { ret = -ENOMEM; str_err = "Unable to allocate receive socket buffer"; goto process_netlink_msg_exit; } /* receive a new message */ nlh = (struct nlmsghdr *)recv_buffer; memset(nlh, 0, NLMSG_SPACE(IWARP_PM_RECV_PAYLOAD)); memset(&src_addr, 0, sizeof(src_addr)); src_addr_len = sizeof(src_addr); len = recvfrom(nl_sock, (void *)nlh, NLMSG_SPACE(IWARP_PM_RECV_PAYLOAD), 0, (struct sockaddr *)&src_addr, &src_addr_len); if (len <= 0) { ret = -errno; str_err = "Unable to receive data from netlink socket"; goto process_netlink_msg_exit; } /* loop for multiple netlink messages packed together */ while (NLMSG_OK(nlh, len) != 0) { if (nlh->nlmsg_type == NLMSG_DONE) { goto process_netlink_msg_exit; } type = nlh->nlmsg_type; client_idx = RDMA_NL_GET_CLIENT(type); if (type == NLMSG_ERROR) { /* RDMA_NL_IWCM HELLO error indicates V3 kernel */ if (nlh->nlmsg_seq == 0) { ret = process_iwpm_hello(nlh, client_idx, nl_sock); } else { iwpm_debug(IWARP_PM_NETLINK_DBG, "process_netlink_msg: " "Netlink error message seq = %u\n", nlh->nlmsg_seq); } goto process_netlink_msg_exit; } op = RDMA_NL_GET_OP(type); iwpm_debug(IWARP_PM_NETLINK_DBG, "process_netlink_msg: Received a new message: " "opcode = %u client idx = %u, client pid = %u," " msg seq = %u, type = %u, length = %u.\n", op, client_idx, nlh->nlmsg_pid, nlh->nlmsg_seq, type, len); if (client_idx >= IWARP_PM_MAX_CLIENTS) { ret = -EINVAL; str_err = "Invalid client index"; goto process_netlink_msg_exit; } switch (op) { case RDMA_NL_IWPM_REG_PID: str_err = "Register Pid request"; ret = process_iwpm_register_pid(nlh, client_idx, nl_sock); break; case RDMA_NL_IWPM_ADD_MAPPING: str_err = "Add Mapping request"; if (!client_list[client_idx].valid) { ret = -EINVAL; goto process_netlink_msg_exit; } ret = process_iwpm_add_mapping(nlh, client_idx, nl_sock); break; case RDMA_NL_IWPM_QUERY_MAPPING: str_err = "Query Mapping request"; if (!client_list[client_idx].valid) { ret = -EINVAL; goto process_netlink_msg_exit; } ret = process_iwpm_query_mapping(nlh, client_idx, nl_sock); break; case RDMA_NL_IWPM_REMOVE_MAPPING: str_err = "Remove Mapping request"; ret = process_iwpm_remove_mapping(nlh, client_idx, nl_sock); break; case RDMA_NL_IWPM_MAPINFO: ret = process_iwpm_mapinfo(nlh, client_idx, nl_sock); break; case RDMA_NL_IWPM_MAPINFO_NUM: ret = process_iwpm_mapinfo_count(nlh, client_idx, nl_sock); break; case RDMA_NL_IWPM_HELLO: ret = process_iwpm_hello(nlh, client_idx, nl_sock); break; default: str_err = "Netlink message with invalid opcode"; ret = -1; break; } nlh = NLMSG_NEXT(nlh, len); if (ret) goto process_netlink_msg_exit; } process_netlink_msg_exit: if (recv_buffer) free(recv_buffer); if (ret) syslog(LOG_WARNING, "process_netlink_msg: %s error (ret = %d).\n", str_err, ret); return ret; } /** * process_iwpm_msg - Dispatch iwpm wire messages, sent by the remote peer * @pm_sock: socket handle to read the messages from */ static int process_iwpm_msg(int pm_sock) { iwpm_msg_parms msg_parms; struct sockaddr_storage recv_addr; iwpm_wire_msg recv_buffer; /* received message */ int bytes_recv, ret = 0; int max_bytes_send = IWARP_PM_MESSAGE_SIZE + IWPM_IPADDR_SIZE; socklen_t recv_addr_len = sizeof(recv_addr); bytes_recv = recvfrom(pm_sock, &recv_buffer, max_bytes_send, 0, (struct sockaddr *)&recv_addr, &recv_addr_len); if (bytes_recv != IWARP_PM_MESSAGE_SIZE && bytes_recv != max_bytes_send) { syslog(LOG_WARNING, "process_iwpm_msg: Unable to receive data from PM socket. %s.\n", strerror(errno)); ret = -errno; goto process_iwpm_msg_exit; } ret = parse_iwpm_msg(&recv_buffer, &msg_parms); if (ret) goto process_iwpm_msg_exit; switch (msg_parms.mt) { case IWARP_PM_MT_REQ: iwpm_debug(IWARP_PM_WIRE_DBG, "process_iwpm_msg: Received Request message.\n"); ret = process_iwpm_wire_request(&msg_parms, netlink_sock, &recv_addr, pm_sock); break; case IWARP_PM_MT_ACK: iwpm_debug(IWARP_PM_WIRE_DBG, "process_iwpm_msg: Received Acknowledgement.\n"); ret = process_iwpm_wire_ack(&msg_parms); break; case IWARP_PM_MT_ACC: iwpm_debug(IWARP_PM_WIRE_DBG, "process_iwpm_msg: Received Accept message.\n"); ret = process_iwpm_wire_accept(&msg_parms, netlink_sock, &recv_addr, pm_sock); break; case IWARP_PM_MT_REJ: iwpm_debug(IWARP_PM_WIRE_DBG, "process_iwpm_msg: Received Reject message.\n"); ret = process_iwpm_wire_reject(&msg_parms, netlink_sock); break; default: syslog(LOG_WARNING, "process_iwpm_msg: Received Invalid message type = %u.\n", msg_parms.mt); } process_iwpm_msg_exit: return ret; } /** * send_iwpm_hello - Notify the client that the V4 iwarp port mapper is available * @nl_sock: netlink socket to send a message to the client * * Send a HELLO message including the ABI_VERSION supported by iwpmd. If the * response is an ERROR message, then we know the kernel driver is < V4, so we * drop back to the V3 protocol. If the kernel is >= V4, then it will reply * with its ABI Version. The response is handled in iwarp_port_mapper(). Once * the ABI version is negotiatied, iwpmd will send a mapinfo request to get any * current mappings, using the correct ABI version. This allows working with * V3 kernels. */ static int send_iwpm_hello(int nl_sock) { struct nl_msg *req_nlmsg; const char *str_err; __u16 nlmsg_type; int ret; nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_IWCM, RDMA_NL_IWPM_HELLO); req_nlmsg = create_iwpm_nlmsg(nlmsg_type, RDMA_NL_IWCM); if (!req_nlmsg) { ret = -ENOMEM; str_err = "Unable to create nlmsg request"; goto send_hello_error; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_u16(req_nlmsg, IWPM_NLA_HELLO_ABI_VERSION, iwpm_version))) goto send_hello_error; if ((ret = send_iwpm_nlmsg(nl_sock, req_nlmsg, 0))) { str_err = "Unable to send nlmsg response"; goto send_hello_error; } nlmsg_free(req_nlmsg); return 0; send_hello_error: if (req_nlmsg) nlmsg_free(req_nlmsg); syslog(LOG_WARNING, "send_hello_request: %s ret = %d.\n", str_err, ret); return ret; } /** * send_iwpm_mapinfo_request - Notify the client that the iwarp port mapper is available * @nl_sock: netlink socket to send a message to the client * @client - client to receive the message */ static int send_iwpm_mapinfo_request(int nl_sock, int client) { struct nl_msg *req_nlmsg; __u16 nlmsg_type; const char *str_err; int ret; nlmsg_type = RDMA_NL_GET_TYPE(client, RDMA_NL_IWPM_MAPINFO); req_nlmsg = create_iwpm_nlmsg(nlmsg_type, client); if (!req_nlmsg) { ret = -ENOMEM; str_err = "Unable to create nlmsg request"; goto send_mapinfo_error; } str_err = "Invalid nlmsg attribute"; if ((ret = nla_put_string(req_nlmsg, IWPM_NLA_MAPINFO_ULIB_NAME, iwpm_ulib_name))) goto send_mapinfo_error; if ((ret = nla_put_u16(req_nlmsg, IWPM_NLA_MAPINFO_ULIB_VER, iwpm_version))) goto send_mapinfo_error; if ((ret = send_iwpm_nlmsg(nl_sock, req_nlmsg, 0))) { str_err = "Unable to send nlmsg response"; goto send_mapinfo_error; } nlmsg_free(req_nlmsg); return 0; send_mapinfo_error: if (req_nlmsg) nlmsg_free(req_nlmsg); syslog(LOG_WARNING, "send_mapinfo_request: %s ret = %d.\n", str_err, ret); return ret; } /** iwpm_cleanup - Close socket handles and free mapped ports */ static void iwpm_cleanup(void) { free_iwpm_mapped_ports(); destroy_iwpm_socket(netlink_sock); destroy_iwpm_socket(pmv6_client_sock); destroy_iwpm_socket(pmv6_sock); destroy_iwpm_socket(pmv4_client_sock); destroy_iwpm_socket(pmv4_sock); /* close up logging */ closelog(); } /** * iwarp_port_mapper - Distribute work orders for processing different types of iwpm messages */ static int iwarp_port_mapper(void) { fd_set select_fdset; /* read fdset */ struct timeval select_timeout; int select_rc, max_sock = 0, ret = 0; if (pmv4_sock > max_sock) max_sock = pmv4_sock; if (pmv6_sock > max_sock) max_sock = pmv6_sock; if (netlink_sock > max_sock) max_sock = netlink_sock; if (pmv4_client_sock > max_sock) max_sock = pmv4_client_sock; if (pmv6_client_sock > max_sock) max_sock = pmv6_client_sock; /* poll a set of sockets */ do { do { if (print_mappings) { print_iwpm_mapped_ports(); print_mappings = 0; } /* initialize the file sets for select */ FD_ZERO(&select_fdset); /* add the UDP and Netlink sockets to the file set */ if (pmv4_sock >= 0) { FD_SET(pmv4_sock, &select_fdset); FD_SET(pmv4_client_sock, &select_fdset); } if (pmv6_sock >= 0) { FD_SET(pmv6_sock, &select_fdset); FD_SET(pmv6_client_sock, &select_fdset); } FD_SET(netlink_sock, &select_fdset); /* set the timeout for select */ select_timeout.tv_sec = 10; select_timeout.tv_usec = 0; /* timeout is an upper bound of time elapsed before select returns */ select_rc = select(max_sock + 1, &select_fdset, NULL, NULL, &select_timeout); } while (select_rc == 0); /* select_rc is the number of fds ready for IO ( IO won't block) */ if (select_rc == -1) { if (errno == EINTR) continue; syslog(LOG_WARNING, "iwarp_port_mapper: Select failed (%s).\n", strerror(errno)); ret = -errno; goto iwarp_port_mapper_exit; } if (pmv4_sock >= 0) { if (FD_ISSET(pmv4_sock, &select_fdset)) ret = process_iwpm_msg(pmv4_sock); if (FD_ISSET(pmv4_client_sock, &select_fdset)) ret = process_iwpm_msg(pmv4_client_sock); } if (pmv6_sock >= 0) { if (FD_ISSET(pmv6_sock, &select_fdset)) ret = process_iwpm_msg(pmv6_sock); if (FD_ISSET(pmv6_client_sock, &select_fdset)) ret = process_iwpm_msg(pmv6_client_sock); } if (FD_ISSET(netlink_sock, &select_fdset)) ret = process_iwpm_netlink_msg(netlink_sock); } while (1); iwarp_port_mapper_exit: return ret; } /** * daemonize_iwpm_server - Make iwarp port mapper a daemon process */ static void daemonize_iwpm_server(void) { if (daemon(0, 0) != 0) { syslog(LOG_ERR, "Failed to daemonize\n"); exit(EXIT_FAILURE); } syslog(LOG_WARNING, "daemonize_iwpm_server: Starting iWarp Port Mapper V%d process\n", iwpm_version); } int main(int argc, char *argv[]) { FILE *fp; int c; int ret = EXIT_FAILURE; bool systemd = false; while (1) { static const struct option long_opts[] = { {"systemd", 0, NULL, 's'}, {} }; c = getopt_long(argc, argv, "fs", long_opts, NULL); if (c == -1) break; switch (c) { case 's': systemd = true; break; default: break; } } openlog(NULL, LOG_NDELAY | LOG_CONS | LOG_PID, LOG_DAEMON); if (!systemd) daemonize_iwpm_server(); umask(0); /* change file mode mask */ fp = fopen(IWPM_CONFIG_FILE, "r"); if (fp) { parse_iwpm_config(fp); fclose(fp); } memset(client_list, 0, sizeof(client_list)); pmv4_client_sock = -1; pmv6_sock = -1; pmv6_client_sock = pmv6_sock; pmv4_sock = create_iwpm_socket_v4(IWARP_PM_PORT); if (pmv4_sock < 0 && pmv4_sock != -EAFNOSUPPORT) goto error_exit_sock; pmv6_sock = create_iwpm_socket_v6(IWARP_PM_PORT); if (pmv6_sock < 0 && pmv6_sock != -EAFNOSUPPORT) goto error_exit_sock; /* If neither IPv4 nor IPv6 is supported, exit */ if (pmv4_sock < 0 && pmv6_sock < 0) goto error_exit_sock; if (pmv4_sock >= 0) { pmv4_client_sock = create_iwpm_socket_v4(0); if (pmv4_client_sock < 0) goto error_exit_sock; } if (pmv6_sock >= 0) { pmv6_client_sock = create_iwpm_socket_v6(0); if (pmv6_client_sock < 0) goto error_exit_sock; } netlink_sock = create_netlink_socket(); if (netlink_sock < 0) goto error_exit_sock; signal(SIGHUP, iwpm_signal_handler); signal(SIGTERM, iwpm_signal_handler); signal(SIGUSR1, iwpm_signal_handler); pthread_cond_init(&cond_req_complete, NULL); pthread_cond_init(&cond_pending_msg, NULL); ret = pthread_create(&map_req_thread, NULL, iwpm_mapping_reqs_handler, NULL); if (ret) goto error_exit; ret = pthread_create(&pending_msg_thread, NULL, iwpm_pending_msgs_handler, NULL); if (ret) goto error_exit; ret = send_iwpm_hello(netlink_sock); if (ret) goto error_exit; if (systemd) sd_notify(0, "READY=1"); iwarp_port_mapper(); /* start iwarp port mapper process */ free_iwpm_mapped_ports(); closelog(); error_exit: destroy_iwpm_socket(netlink_sock); error_exit_sock: destroy_iwpm_socket(pmv4_client_sock); destroy_iwpm_socket(pmv6_client_sock); destroy_iwpm_socket(pmv4_sock); destroy_iwpm_socket(pmv6_sock); syslog(LOG_WARNING, "main: Couldn't start iWarp Port Mapper.\n"); return ret; }