/* * Copyright (c) 2013-2015 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 "iwarp_pm.h" #include /* iwpm config params */ static const char * iwpm_param_names[IWPM_PARAM_NUM] = { "nl_sock_rbuf_size" }; static int iwpm_param_vals[IWPM_PARAM_NUM] = { 0 }; /** * get_iwpm_param() */ static int get_iwpm_param(char *param_name, int val) { int i, ret; for (i = 0; i < IWPM_PARAM_NUM; i++) { ret = strcmp(param_name, iwpm_param_names[i]); if (!ret && val > 0) { syslog(LOG_WARNING, "get_iwpm_param: Got param (name = %s val = %d)\n", param_name, val); iwpm_param_vals[i] = val; return ret; } } return ret; } /** * parse_iwpm_config() */ void parse_iwpm_config(FILE *fp) { char line_buf[128]; char param_name[IWPM_PARAM_NAME_LEN]; int n, val, ret; char *str; str = fgets(line_buf, 128, fp); while (str) { if (line_buf[0] == '#' || line_buf[0] == '\n') goto parse_next_line; n = sscanf(line_buf, "%63[^= ] %*[=]%d", param_name, &val); if (n != 2) { syslog(LOG_WARNING, "parse_iwpm_config: Couldn't parse a line (n = %d, name = %s, val = %d\n", n, param_name, val); goto parse_next_line; } ret = get_iwpm_param(param_name, val); if (ret) syslog(LOG_WARNING, "parse_iwpm_config: Couldn't find param (ret = %d)\n", ret); parse_next_line: str = fgets(line_buf, 128, fp); } } /** * create_iwpm_socket_v4 - Create an ipv4 socket for the iwarp port mapper * @bind_port: UDP port to bind the socket * * Return a handle of ipv4 socket */ int create_iwpm_socket_v4(__u16 bind_port) { sockaddr_union bind_addr; struct sockaddr_in *bind_in4; int pm_sock; socklen_t sockname_len; char ip_address_text[INET6_ADDRSTRLEN]; /* create a socket */ pm_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (pm_sock < 0) { syslog(LOG_WARNING, "create_iwpm_socket_v4: Unable to create socket. %s.\n", strerror(errno)); pm_sock = -errno; goto create_socket_v4_exit; } /* bind the socket to the given port */ memset(&bind_addr, 0, sizeof(bind_addr)); bind_in4 = &bind_addr.v4_sockaddr; bind_in4->sin_family = AF_INET; bind_in4->sin_addr.s_addr = htobe32(INADDR_ANY); bind_in4->sin_port = htobe16(bind_port); if (bind(pm_sock, &bind_addr.sock_addr, sizeof(struct sockaddr_in))) { syslog(LOG_WARNING, "create_iwpm_socket_v4: Unable to bind socket (port = %u). %s.\n", bind_port, strerror(errno)); close(pm_sock); pm_sock = -errno; goto create_socket_v4_exit; } /* get the socket name (local port number) */ sockname_len = sizeof(struct sockaddr_in); if (getsockname(pm_sock, &bind_addr.sock_addr, &sockname_len)) { syslog(LOG_WARNING, "create_iwpm_socket_v4: Unable to get socket name. %s.\n", strerror(errno)); close(pm_sock); pm_sock = -errno; goto create_socket_v4_exit; } iwpm_debug(IWARP_PM_WIRE_DBG, "create_iwpm_socket_v4: Socket IP address:port %s:%u\n", inet_ntop(bind_in4->sin_family, &bind_in4->sin_addr.s_addr, ip_address_text, INET6_ADDRSTRLEN), be16toh(bind_in4->sin_port)); create_socket_v4_exit: return pm_sock; } /** * create_iwpm_socket_v6 - Create an ipv6 socket for the iwarp port mapper * @bind_port: UDP port to bind the socket * * Return a handle of ipv6 socket */ int create_iwpm_socket_v6(__u16 bind_port) { sockaddr_union bind_addr; struct sockaddr_in6 *bind_in6; int pm_sock, ret_value, ipv6_only; socklen_t sockname_len; char ip_address_text[INET6_ADDRSTRLEN]; /* create a socket */ pm_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (pm_sock < 0) { syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to create socket. %s.\n", strerror(errno)); pm_sock = -errno; goto create_socket_v6_exit; } ipv6_only = 1; ret_value = setsockopt(pm_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)); if (ret_value < 0) { syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to set sock options. %s.\n", strerror(errno)); close(pm_sock); pm_sock = -errno; goto create_socket_v6_exit; } /* bind the socket to the given port */ memset(&bind_addr, 0, sizeof(bind_addr)); bind_in6 = &bind_addr.v6_sockaddr; bind_in6->sin6_family = AF_INET6; bind_in6->sin6_addr = in6addr_any; bind_in6->sin6_port = htobe16(bind_port); if (bind(pm_sock, &bind_addr.sock_addr, sizeof(struct sockaddr_in6))) { syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to bind socket (port = %u). %s.\n", bind_port, strerror(errno)); close(pm_sock); pm_sock = -errno; goto create_socket_v6_exit; } /* get the socket name (local port number) */ sockname_len = sizeof(struct sockaddr_in6); if (getsockname(pm_sock, &bind_addr.sock_addr, &sockname_len)) { syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to get socket name. %s.\n", strerror(errno)); close(pm_sock); pm_sock = -errno; goto create_socket_v6_exit; } iwpm_debug(IWARP_PM_WIRE_DBG, "create_iwpm_socket_v6: Socket IP address:port %s:%04X\n", inet_ntop(bind_in6->sin6_family, &bind_in6->sin6_addr, ip_address_text, INET6_ADDRSTRLEN), be16toh(bind_in6->sin6_port)); create_socket_v6_exit: return pm_sock; } /** * create_netlink_socket - Create netlink socket for the iwarp port mapper */ int create_netlink_socket(void) { sockaddr_union bind_addr; struct sockaddr_nl *bind_nl; int nl_sock; __u32 rbuf_size, opt_len; /* create a socket */ nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA); if (nl_sock < 0) { syslog(LOG_WARNING, "create_netlink_socket: Unable to create socket. %s.\n", strerror(errno)); nl_sock = -errno; goto create_nl_socket_exit; } /* bind the socket */ memset(&bind_addr, 0, sizeof(bind_addr)); bind_nl = &bind_addr.nl_sockaddr; bind_nl->nl_family = AF_NETLINK; bind_nl->nl_pid = getpid(); bind_nl->nl_groups = 3; /* != 0 support multicast */ if (bind(nl_sock, &bind_addr.sock_addr, sizeof(struct sockaddr_nl))) { syslog(LOG_WARNING, "create_netlink_socket: Unable to bind socket. %s.\n", strerror(errno)); close(nl_sock); nl_sock = -errno; goto create_nl_socket_exit; } if (iwpm_param_vals[NL_SOCK_RBUF_SIZE] > 0) { rbuf_size = iwpm_param_vals[NL_SOCK_RBUF_SIZE]; if (setsockopt(nl_sock, SOL_SOCKET, SO_RCVBUFFORCE, &rbuf_size, sizeof rbuf_size)) { syslog(LOG_WARNING, "create_netlink_socket: Unable to set sock option " "(rbuf_size = %u). %s.\n", rbuf_size, strerror(errno)); if (setsockopt(nl_sock, SOL_SOCKET, SO_RCVBUF, &rbuf_size, sizeof rbuf_size)) { syslog(LOG_WARNING, "create_netlink_socket: " "Unable to set sock option %s. Closing socket\n", strerror(errno)); close(nl_sock); nl_sock = -errno; goto create_nl_socket_exit; } } } getsockopt(nl_sock, SOL_SOCKET, SO_RCVBUF, &rbuf_size, &opt_len); iwpm_debug(IWARP_PM_NETLINK_DBG, "create_netlink_socket: Setting a sock option (rbuf_size = %u).\n", rbuf_size); create_nl_socket_exit: return nl_sock; } /** * destroy_iwpm_socket - Close socket */ void destroy_iwpm_socket(int pm_sock) { if (pm_sock >= 0) close(pm_sock); pm_sock = -1; } /** * check_iwpm_nlattr - Check for NULL netlink attribute */ static int check_iwpm_nlattr(struct nlattr *nltb[], int nla_count) { int i, ret = 0; for (i = 1; i < nla_count; i++) { if (!nltb[i]) { iwpm_debug(IWARP_PM_NETLINK_DBG, "check_iwpm_nlattr: NULL (attr idx = %d)\n", i); ret = -EINVAL; } } return ret; } /** * parse_iwpm_nlmsg - Parse a netlink message * @req_nlh: netlink header of the received message to parse * @policy_max: the number of attributes in the policy * @nlmsg_policy: the attribute policy * @nltb: array to store the parsed attributes * @msg_type: netlink message type (dbg purpose) */ int parse_iwpm_nlmsg(struct nlmsghdr *req_nlh, int policy_max, struct nla_policy *nlmsg_policy, struct nlattr *nltb [], const char *msg_type) { const char *str_err; int ret; if ((ret = nlmsg_validate(req_nlh, 0, policy_max-1, nlmsg_policy))) { str_err = "nlmsg_validate error"; goto parse_nlmsg_error; } if ((ret = nlmsg_parse(req_nlh, 0, nltb, policy_max-1, nlmsg_policy))) { str_err = "nlmsg_parse error"; goto parse_nlmsg_error; } if (check_iwpm_nlattr(nltb, policy_max)) { ret = -EINVAL; str_err = "NULL nlmsg attribute"; goto parse_nlmsg_error; } return 0; parse_nlmsg_error: syslog(LOG_WARNING, "parse_iwpm_nlmsg: msg type = %s (%s ret = %d)\n", msg_type, str_err, ret); return ret; } /** * send_iwpm_nlmsg - Send a netlink message * @nl_sock: netlink socket to use for sending the message * @nlmsg: netlink message to send * @dest_pid: pid of the destination of the nlmsg */ int send_iwpm_nlmsg(int nl_sock, struct nl_msg *nlmsg, int dest_pid) { struct sockaddr_nl dest_addr; struct nlmsghdr *nlh = nlmsg_hdr(nlmsg); __u32 nlmsg_len = nlh->nlmsg_len; int len; /* fill in the netlink address of the client */ memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_groups = 0; dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = dest_pid; /* send response to the client */ len = sendto(nl_sock, (char *)nlh, nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (len != nlmsg_len) return -errno; return 0; } /** * create_iwpm_nlmsg - Create a netlink message * @nlmsg_type: type of the netlink message * @client: the port mapper client to receive the message */ struct nl_msg *create_iwpm_nlmsg(__u16 nlmsg_type, int client_idx) { struct nl_msg *nlmsg; struct nlmsghdr *nlh; __u32 seq = 0; nlmsg = nlmsg_alloc(); if (!nlmsg) return NULL; if (client_idx > 0) seq = client_list[client_idx].nl_seq++; nlh = nlmsg_put(nlmsg, getpid(), seq, nlmsg_type, 0, NLM_F_REQUEST); if (!nlh) { nlmsg_free(nlmsg); return NULL; } return nlmsg; } /** * parse_iwpm_msg - Parse iwarp port mapper wire message * @pm_msg: iwpm message to be parsed * @msg_parms: contains the parameters of the iwpm message after parsing */ int parse_iwpm_msg(iwpm_wire_msg *pm_msg, iwpm_msg_parms *msg_parms) { int ret_value = 0; msg_parms->pmtime = pm_msg->pmtime; msg_parms->assochandle = be64toh(pm_msg->assochandle); msg_parms->ip_ver = (pm_msg->magic & IWARP_PM_IPVER_MASK) >> IWARP_PM_IPVER_SHIFT; switch (msg_parms->ip_ver) { case 4: msg_parms->address_family = AF_INET; break; case 6: msg_parms->address_family = AF_INET6; break; default: syslog(LOG_WARNING, "parse_iwpm_msg: Invalid IP version = %d.\n", msg_parms->ip_ver); return -EINVAL; } /* port mapper protocol version */ msg_parms->ver = (pm_msg->magic & IWARP_PM_VER_MASK) >> IWARP_PM_VER_SHIFT; /* message type */ msg_parms->mt = (pm_msg->magic & IWARP_PM_MT_MASK) >> IWARP_PM_MT_SHIFT; msg_parms->apport = pm_msg->apport; /* accepting peer port */ msg_parms->cpport = pm_msg->cpport; /* connecting peer port */ /* copy accepting peer IP address */ memcpy(&msg_parms->apipaddr, &pm_msg->apipaddr, IWPM_IPADDR_SIZE); /* copy connecting peer IP address */ memcpy(&msg_parms->cpipaddr, &pm_msg->cpipaddr, IWPM_IPADDR_SIZE); if (msg_parms->mt == IWARP_PM_MT_REQ) { msg_parms->mapped_cpport = pm_msg->reserved; memcpy(&msg_parms->mapped_cpipaddr, &pm_msg->mapped_cpipaddr, IWPM_IPADDR_SIZE); } return ret_value; } /** * form_iwpm_msg - Form iwarp port mapper wire message * @pm_msg: iwpm message to be formed * @msg_parms: the parameters to be packed in a iwpm message */ static void form_iwpm_msg(iwpm_wire_msg *pm_msg, iwpm_msg_parms *msg_parms) { memset(pm_msg, 0, sizeof(struct iwpm_wire_msg)); pm_msg->pmtime = msg_parms->pmtime; pm_msg->assochandle = htobe64(msg_parms->assochandle); /* record IP version, port mapper version, message type */ pm_msg->magic = (msg_parms->ip_ver << IWARP_PM_IPVER_SHIFT) & IWARP_PM_IPVER_MASK; pm_msg->magic |= (msg_parms->ver << IWARP_PM_VER_SHIFT) & IWARP_PM_VER_MASK; pm_msg->magic |= (msg_parms->mt << IWARP_PM_MT_SHIFT) & IWARP_PM_MT_MASK; pm_msg->apport = msg_parms->apport; pm_msg->cpport = msg_parms->cpport; memcpy(&pm_msg->apipaddr, &msg_parms->apipaddr, IWPM_IPADDR_SIZE); memcpy(&pm_msg->cpipaddr, &msg_parms->cpipaddr, IWPM_IPADDR_SIZE); if (msg_parms->mt == IWARP_PM_MT_REQ) { pm_msg->reserved = msg_parms->mapped_cpport; memcpy(&pm_msg->mapped_cpipaddr, &msg_parms->mapped_cpipaddr, IWPM_IPADDR_SIZE); } } /** * form_iwpm_request - Form iwarp port mapper request message * @pm_msg: iwpm message to be formed * @msg_parms: the parameters to be packed in a iwpm message **/ void form_iwpm_request(struct iwpm_wire_msg *pm_msg, struct iwpm_msg_parms *msg_parms) { msg_parms->mt = IWARP_PM_MT_REQ; msg_parms->msize = IWARP_PM_MESSAGE_SIZE + IWPM_IPADDR_SIZE; form_iwpm_msg(pm_msg, msg_parms); } /** * form_iwpm_accept - Form iwarp port mapper accept message * @pm_msg: iwpm message to be formed * @msg_parms: the parameters to be packed in a iwpm message **/ void form_iwpm_accept(struct iwpm_wire_msg *pm_msg, struct iwpm_msg_parms *msg_parms) { msg_parms->mt = IWARP_PM_MT_ACC; msg_parms->msize = IWARP_PM_MESSAGE_SIZE; form_iwpm_msg(pm_msg, msg_parms); } /** * form_iwpm_ack - Form iwarp port mapper ack message * @pm_msg: iwpm message to be formed * @msg_parms: the parameters to be packed in a iwpm message **/ void form_iwpm_ack(struct iwpm_wire_msg *pm_msg, struct iwpm_msg_parms *msg_parms) { msg_parms->mt = IWARP_PM_MT_ACK; msg_parms->msize = IWARP_PM_MESSAGE_SIZE; form_iwpm_msg(pm_msg, msg_parms); } /** * form_iwpm_reject - Form iwarp port mapper reject message * @pm_msg: iwpm message to be formed * @msg_parms: the parameters to be packed in a iwpm message */ void form_iwpm_reject(struct iwpm_wire_msg *pm_msg, struct iwpm_msg_parms *msg_parms) { msg_parms->mt = IWARP_PM_MT_REJ; msg_parms->msize = IWARP_PM_MESSAGE_SIZE; form_iwpm_msg(pm_msg, msg_parms); } /** * get_sockaddr_port - Report the tcp port number, contained in the sockaddr * @sockaddr: sockaddr storage to get the tcp port from */ __be16 get_sockaddr_port(struct sockaddr_storage *sockaddr) { struct sockaddr_in *sockaddr_v4; struct sockaddr_in6 *sockaddr_v6; __be16 port = 0; switch (sockaddr->ss_family) { case AF_INET: sockaddr_v4 = (struct sockaddr_in *)sockaddr; port = sockaddr_v4->sin_port; break; case AF_INET6: sockaddr_v6 = (struct sockaddr_in6 *)sockaddr; port = sockaddr_v6->sin6_port; break; default: syslog(LOG_WARNING, "get_sockaddr_port: Invalid sockaddr family.\n"); break; } return port; } /** * copy_iwpm_sockaddr - Copy (IP address and Port) from src to dst * @address_family: Internet address family * @src_sockaddr: socket address to copy (if NULL, use src_addr) * @dst_sockaddr: socket address to update (if NULL, use dst_addr) * @src_addr: IP address to copy (if NULL, use src_sockaddr) * @dst_addr: IP address to update (if NULL, use dst_sockaddr) * @src_port: port to copy in dst_sockaddr, if src_sockaddr = NULL * port to update, if src_sockaddr != NULL and dst_sockaddr = NULL */ void copy_iwpm_sockaddr(__u16 addr_family, struct sockaddr_storage *src_sockaddr, struct sockaddr_storage *dst_sockaddr, char *src_addr, char *dst_addr, __be16 *src_port) { switch (addr_family) { case AF_INET: { const struct in_addr *src = (void *)src_addr; struct in_addr *dst = (void *)dst_addr; const struct sockaddr_in *src_sockaddr_in; struct sockaddr_in *dst_sockaddr_in; if (src_sockaddr) { src_sockaddr_in = (const void *)src_sockaddr; src = &src_sockaddr_in->sin_addr; *src_port = src_sockaddr_in->sin_port; } if (dst_sockaddr) { dst_sockaddr_in = (void *)dst_sockaddr; dst = &dst_sockaddr_in->sin_addr; dst_sockaddr_in->sin_port = *src_port; dst_sockaddr_in->sin_family = AF_INET; } *dst = *src; break; } case AF_INET6: { const struct in6_addr *src = (void *)src_addr; struct in6_addr *dst = (void *)dst_addr; const struct sockaddr_in6 *src_sockaddr_in6; struct sockaddr_in6 *dst_sockaddr_in6; if (src_sockaddr) { src_sockaddr_in6 = (const void *)src_sockaddr; src = &src_sockaddr_in6->sin6_addr; *src_port = src_sockaddr_in6->sin6_port; } if (dst_sockaddr) { dst_sockaddr_in6 = (void *)dst_sockaddr; dst = &dst_sockaddr_in6->sin6_addr; dst_sockaddr_in6->sin6_port = *src_port; dst_sockaddr_in6->sin6_family = AF_INET6; } *dst = *src; break; } default: assert(false); if (dst_sockaddr) dst_sockaddr->ss_family = addr_family; } } /** * is_wcard_ipaddr - Check if the search_addr has a wild card ip address */ int is_wcard_ipaddr(struct sockaddr_storage *search_addr) { int ret = 0; switch (search_addr->ss_family) { case AF_INET: { struct sockaddr_in wcard_addr; struct sockaddr_in *in4addr = (struct sockaddr_in *)search_addr; inet_pton(AF_INET, "0.0.0.0", &wcard_addr.sin_addr); if (in4addr->sin_addr.s_addr == wcard_addr.sin_addr.s_addr) ret = 1; break; } case AF_INET6: { struct sockaddr_in6 wcard_addr; struct sockaddr_in6 *in6addr = (struct sockaddr_in6 *)search_addr; inet_pton(AF_INET6, "::", &wcard_addr.sin6_addr); if (!memcmp(in6addr->sin6_addr.s6_addr, wcard_addr.sin6_addr.s6_addr, IWPM_IPADDR_SIZE)) ret = 1; break; } default: syslog(LOG_WARNING, "check_same_sockaddr: Invalid addr family 0x%02X\n", search_addr->ss_family); break; } return ret; } /** * print_iwpm_sockaddr - Print socket address (IP address and Port) * @sockaddr: socket address to print * @msg: message to print */ void print_iwpm_sockaddr(struct sockaddr_storage *sockaddr, const char *msg, __u32 dbg_flag) { struct sockaddr_in6 *sockaddr_v6; struct sockaddr_in *sockaddr_v4; char ip_address_text[INET6_ADDRSTRLEN]; switch (sockaddr->ss_family) { case AF_INET: sockaddr_v4 = (struct sockaddr_in *)sockaddr; iwpm_debug(dbg_flag, "%s IPV4 %s:%u(0x%04X)\n", msg, inet_ntop(AF_INET, &sockaddr_v4->sin_addr, ip_address_text, INET6_ADDRSTRLEN), be16toh(sockaddr_v4->sin_port), be16toh(sockaddr_v4->sin_port)); break; case AF_INET6: sockaddr_v6 = (struct sockaddr_in6 *)sockaddr; iwpm_debug(dbg_flag, "%s IPV6 %s:%u(0x%04X)\n", msg, inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, ip_address_text, INET6_ADDRSTRLEN), be16toh(sockaddr_v6->sin6_port), be16toh(sockaddr_v6->sin6_port)); break; default: break; } }