/** netlink_policy - * * @param hdr - Data to check * @param enoent_ok - Boolean - OK or not OK. * @param text_said - String * @return boolean */ static bool netlink_policy(struct nlmsghdr *hdr, bool enoent_ok, const char *text_said) { struct { struct nlmsghdr n; struct nlmsgerr e; } rsp; int error; rsp.n.nlmsg_type = NLMSG_ERROR; if (!send_netlink_msg(hdr, &rsp.n, sizeof(rsp), "policy", text_said)) { return FALSE; } error = -rsp.e.error; if (!error) { return TRUE; } if (error == ENOENT && enoent_ok) { return TRUE; } loglog(RC_LOG_SERIOUS , "ERROR: netlink %s response for flow %s included errno %d: %s" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , text_said , error , strerror(error)); return FALSE; }
/* function: send_ifaddrmsg * sends a netlink/ifaddrmsg message and hands the responses to the callbacks * type - netlink message type * flags - netlink message flags * ifa - ifaddrmsg to send * callbacks - callbacks to use with the responses */ void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks) { struct nl_msg *msg = NULL; msg = nlmsg_alloc_ifaddr(type, flags, ifa); if(!msg) return; send_netlink_msg(msg, callbacks); nlmsg_free(msg); }
/* function: netlink_sendrecv * send a nl_msg and return an int status - only supports OK/ERROR responses * msg - msg to send */ int netlink_sendrecv(struct nl_msg *msg) { struct nl_cb *callbacks = NULL; int retval = -EIO; callbacks = alloc_ack_callbacks(&retval); if(!callbacks) { return -ENOMEM; } send_netlink_msg(msg, callbacks); nl_cb_put(callbacks); return retval; }
/* function: get_default_route * finds the first default route with the given family and interface, returns the gateway (if it exists) in the struct * default_route - requested family and interface, and response storage */ int get_default_route(struct default_route_data *default_route) { struct rtmsg msg; struct nl_cb *callbacks = NULL; struct nl_msg *nlmsg = NULL; int retval = 0; default_route->reply_has_gateway = 0; default_route->reply_found_route = 0; memset(&msg,'\0',sizeof(msg)); msg.rtm_family = default_route->request_family; msg.rtm_table = RT_TABLE_MAIN; msg.rtm_protocol = RTPROT_KERNEL; msg.rtm_scope = RT_SCOPE_UNIVERSE; callbacks = nl_cb_alloc(NL_CB_DEFAULT); if(!callbacks) { retval = -ENOMEM; goto cleanup; } // get_default_route_cb sets the response fields in default_route nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, get_default_route_cb, default_route); nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &retval); nlmsg = nlmsg_alloc_rtmsg(RTM_GETROUTE, NLM_F_REQUEST | NLM_F_ROOT, &msg); if(!nlmsg) { retval = -ENOMEM; goto cleanup; } send_netlink_msg(nlmsg, callbacks); cleanup: if(callbacks) nl_cb_put(callbacks); if(nlmsg) nlmsg_free(nlmsg); return retval; }
/** netlink_del_sa - Delete an SA from the Kernel * * @param sa Kernel SA to be deleted * @return bool True if successfull */ static bool netlink_del_sa(const struct kernel_sa *sa) { struct { struct nlmsghdr n; struct xfrm_usersa_id id; char data[1024]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_type = XFRM_MSG_DELSA; ip2xfrm(sa->dst, &req.id.daddr); req.id.spi = sa->spi; req.id.family = sa->src->u.v4.sin_family; req.id.proto = sa->proto; req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id))); return send_netlink_msg(&req.n, NULL, 0, "Del SA", sa->text_said); }
static void netlink_policy_expire(struct nlmsghdr *n) { struct xfrm_user_polexpire *upe; struct { struct nlmsghdr n; struct xfrm_userpolicy_id id; } req; struct { struct nlmsghdr n; struct xfrm_userpolicy_info pol; char data[1024]; } rsp; if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*upe))) { openswan_log("netlink_policy_expire got message with length %lu < %lu bytes; ignore message" , (unsigned long) n->nlmsg_len , (unsigned long) sizeof(*upe)); return; } upe = NLMSG_DATA(n); req.id.dir = upe->pol.dir; req.id.index = upe->pol.index; req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = XFRM_MSG_GETPOLICY; req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id))); rsp.n.nlmsg_type = XFRM_MSG_NEWPOLICY; if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get policy", "?")) { return; } else if (rsp.n.nlmsg_type == NLMSG_ERROR) { DBG(DBG_KLIPS, DBG_log("netlink_policy_expire: policy died on us: " "dir=%d, index=%d" , req.id.dir, req.id.index)); return; } else if (rsp.n.nlmsg_len < NLMSG_LENGTH(sizeof(rsp.pol))) { openswan_log("netlink_policy_expire: XFRM_MSG_GETPOLICY returned message with length %lu < %lu bytes; ignore message" , (unsigned long) rsp.n.nlmsg_len , (unsigned long) sizeof(rsp.pol)); return; } else if (req.id.index != rsp.pol.index) { DBG(DBG_KLIPS, DBG_log("netlink_policy_expire: policy was replaced: " "dir=%d, oldindex=%d, newindex=%d" , req.id.dir, req.id.index, rsp.pol.index)); return; } else if (upe->pol.curlft.add_time != rsp.pol.curlft.add_time) { DBG(DBG_KLIPS, DBG_log("netlink_policy_expire: policy was replaced " " and you have won the lottery: " "dir=%d, index=%d" , req.id.dir, req.id.index)); return; } switch (upe->pol.dir) { case XFRM_POLICY_OUT: netlink_shunt_expire(&rsp.pol); break; } }
/** netlink_add_sa - Add an SA into the kernel SPDB via netlink * * @param sa Kernel SA to add/modify * @param replace boolean - true if this replaces an existing SA * @return bool True if successfull */ static bool netlink_add_sa(const struct kernel_sa *sa, bool replace) { struct { struct nlmsghdr n; struct xfrm_usersa_info p; char data[1024]; } req; struct rtattr *attr; memset(&req, 0, sizeof(req)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.n.nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; ip2xfrm(sa->src, &req.p.saddr); ip2xfrm(sa->dst, &req.p.id.daddr); req.p.id.spi = sa->spi; req.p.id.proto = satype2proto(sa->satype); req.p.family = sa->src->u.v4.sin_family; req.p.mode = (sa->encapsulation == ENCAPSULATION_MODE_TUNNEL); req.p.replay_window = sa->replay_window; req.p.reqid = sa->reqid; req.p.lft.soft_byte_limit = XFRM_INF; req.p.lft.soft_packet_limit = XFRM_INF; req.p.lft.hard_byte_limit = XFRM_INF; req.p.lft.hard_packet_limit = XFRM_INF; req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.p))); attr = (struct rtattr *)((char *)&req + req.n.nlmsg_len); if (sa->authkeylen) { struct xfrm_algo algo; const char *name; name = sparse_name(aalg_list, sa->authalg); if (!name) { loglog(RC_LOG_SERIOUS, "unknown authentication algorithm: %u" , sa->authalg); return FALSE; } strcpy(algo.alg_name, name); algo.alg_key_len = sa->authkeylen * BITS_PER_BYTE; attr->rta_type = XFRMA_ALG_AUTH; attr->rta_len = RTA_LENGTH(sizeof(algo) + sa->authkeylen); memcpy(RTA_DATA(attr), &algo, sizeof(algo)); memcpy((char *)RTA_DATA(attr) + sizeof(algo), sa->authkey , sa->authkeylen); req.n.nlmsg_len += attr->rta_len; attr = (struct rtattr *)((char *)attr + attr->rta_len); } if (sa->enckeylen) { struct xfrm_algo algo; const char *name; name = sparse_name(ealg_list, sa->encalg); if (!name) { loglog(RC_LOG_SERIOUS, "unknown encryption algorithm: %u" , sa->encalg); return FALSE; } strcpy(algo.alg_name, name); algo.alg_key_len = sa->enckeylen * BITS_PER_BYTE; attr->rta_type = XFRMA_ALG_CRYPT; attr->rta_len = RTA_LENGTH(sizeof(algo) + sa->enckeylen); memcpy(RTA_DATA(attr), &algo, sizeof(algo)); memcpy((char *)RTA_DATA(attr) + sizeof(algo), sa->enckey , sa->enckeylen); req.n.nlmsg_len += attr->rta_len; attr = (struct rtattr *)((char *)attr + attr->rta_len); } if (sa->satype == SADB_X_SATYPE_COMP) { struct xfrm_algo algo; const char *name; name = sparse_name(calg_list, sa->encalg); if (!name) { loglog(RC_LOG_SERIOUS, "unknown compression algorithm: %u" , sa->encalg); return FALSE; } strcpy(algo.alg_name, name); algo.alg_key_len = 0; attr->rta_type = XFRMA_ALG_COMP; attr->rta_len = RTA_LENGTH(sizeof(algo)); memcpy(RTA_DATA(attr), &algo, sizeof(algo)); req.n.nlmsg_len += attr->rta_len; attr = (struct rtattr *)((char *)attr + attr->rta_len); } #ifdef NAT_TRAVERSAL if (sa->natt_type) { struct xfrm_encap_tmpl natt; natt.encap_type = sa->natt_type; natt.encap_sport = ntohs(sa->natt_sport); natt.encap_dport = ntohs(sa->natt_dport); memset (&natt.encap_oa, 0, sizeof (natt.encap_oa)); attr->rta_type = XFRMA_ENCAP; attr->rta_len = RTA_LENGTH(sizeof(natt)); memcpy(RTA_DATA(attr), &natt, sizeof(natt)); req.n.nlmsg_len += attr->rta_len; attr = (struct rtattr *)((char *)attr + attr->rta_len); } #endif return send_netlink_msg(&req.n, NULL, 0, "Add SA", sa->text_said); }
static ipsec_spi_t netlink_get_spi(const ip_address *src , const ip_address *dst , int proto , bool tunnel_mode , unsigned reqid , ipsec_spi_t min , ipsec_spi_t max , const char *text_said) { struct { struct nlmsghdr n; struct xfrm_userspi_info spi; } req; struct { struct nlmsghdr n; union { struct nlmsgerr e; struct xfrm_usersa_info sa; } u; char data[1024]; } rsp; memset(&req, 0, sizeof(req)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = XFRM_MSG_ALLOCSPI; ip2xfrm(src, &req.spi.info.saddr); ip2xfrm(dst, &req.spi.info.id.daddr); req.spi.info.mode = tunnel_mode; req.spi.info.reqid = reqid; req.spi.info.id.proto = proto; req.spi.info.family = src->u.v4.sin_family; req.spi.min = min; req.spi.max = max; req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.spi))); rsp.n.nlmsg_type = XFRM_MSG_NEWSA; if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get SPI", text_said)) return 0; else if (rsp.n.nlmsg_type == NLMSG_ERROR) { loglog(RC_LOG_SERIOUS , "ERROR: netlink_get_spi for %s failed with errno %d: %s" , text_said , -rsp.u.e.error , strerror(-rsp.u.e.error)); return 0; } else if (rsp.n.nlmsg_len < NLMSG_LENGTH(sizeof(rsp.u.sa))) { openswan_log("netlink_get_spi: XFRM_MSG_ALLOCSPI returned message with length %lu < %lu bytes; ignore message" , (unsigned long) rsp.n.nlmsg_len , (unsigned long) sizeof(rsp.u.sa)); return 0; } DBG(DBG_KLIPS, DBG_log("netlink_get_spi: allocated 0x%x for %s" , ntohl(rsp.u.sa.id.spi), text_said)); return rsp.u.sa.id.spi; }