/** 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; }
/* Handle PF_KEY messages from the kernel that are not dealt with * synchronously. In other words, all but responses to PF_KEY messages * that we sent. */ static void pfkey_async(pfkey_buf *buf) { struct sadb_ext *extensions[SADB_EXT_MAX + 1]; if (pfkey_msg_parse(&buf->msg, NULL, extensions, EXT_BITS_OUT)) { plog("pfkey_async:" " unparseable PF_KEY message:" " %s len=%d, errno=%d, seq=%d, pid=%d; message ignored" , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) , buf->msg.sadb_msg_len , buf->msg.sadb_msg_errno , buf->msg.sadb_msg_seq , buf->msg.sadb_msg_pid); } else { DBG(DBG_CONTROL | DBG_KLIPS, DBG_log("pfkey_async:" " %s len=%u, errno=%u, satype=%u, seq=%u, pid=%u" , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) , buf->msg.sadb_msg_len , buf->msg.sadb_msg_errno , buf->msg.sadb_msg_satype , buf->msg.sadb_msg_seq , buf->msg.sadb_msg_pid)); switch (buf->msg.sadb_msg_type) { case SADB_REGISTER: kernel_ops->pfkey_register_response(&buf->msg); break; case SADB_ACQUIRE: /* to simulate loss of ACQUIRE, delete this call */ process_pfkey_acquire(buf, extensions); break; #ifdef NAT_TRAVERSAL case SADB_X_NAT_T_NEW_MAPPING: process_pfkey_nat_t_new_mapping(&(buf->msg), extensions); break; #endif default: /* ignored */ break; } } }
/* Finish (building, sending, accepting response for) PF_KEY message. * If response isn't NULL, the response from the kernel will be * placed there (and its errno field will not be examined). * Returns TRUE iff all appears well. */ static bool finish_pfkey_msg(struct sadb_ext *extensions[SADB_EXT_MAX + 1] , const char *description , const char *text_said , pfkey_buf *response) { struct sadb_msg *pfkey_msg; bool success = TRUE; int error; error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN); if (error != 0) { loglog(RC_LOG_SERIOUS, "pfkey_msg_build of %s %s failed, code %d" , description, text_said, error); success = FALSE; } else { size_t len = pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN; DBG(DBG_KLIPS, DBG_log("finish_pfkey_msg: %s message %u for %s %s" , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type) , pfkey_msg->sadb_msg_seq , description, text_said); DBG_dump(NULL, (void *) pfkey_msg, len)); if (!no_klips) { ssize_t r = write(pfkeyfd, pfkey_msg, len); if (r != (ssize_t)len) { if (r < 0) { log_errno((e , "pfkey write() of %s message %u" " for %s %s failed" , sparse_val_show(pfkey_type_names , pfkey_msg->sadb_msg_type) , pfkey_msg->sadb_msg_seq , description, text_said)); } else { loglog(RC_LOG_SERIOUS , "ERROR: pfkey write() of %s message %u" " for %s %s truncated: %ld instead of %ld" , sparse_val_show(pfkey_type_names , pfkey_msg->sadb_msg_type) , pfkey_msg->sadb_msg_seq , description, text_said , (long)r, (long)len); } success = FALSE; /* if we were compiled with debugging, but we haven't already * dumped the KLIPS command, do so. */ #ifdef DEBUG if ((cur_debugging & DBG_KLIPS) == 0) DBG_dump(NULL, (void *) pfkey_msg, len); #endif } else { /* Check response from KLIPS. * It ought to be an echo, perhaps with additional info. * If the caller wants it, response will point to space. */ pfkey_buf b; pfkey_buf *bp = response != NULL? response : &b; if (!pfkey_get_response(bp, ((struct sadb_msg *) extensions[0])->sadb_msg_seq)) { loglog(RC_LOG_SERIOUS , "ERROR: no response to our PF_KEY %s message for %s %s" , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type) , description, text_said); success = FALSE; } else if (pfkey_msg->sadb_msg_type != bp->msg.sadb_msg_type) { loglog(RC_LOG_SERIOUS , "FreeS/WAN ERROR: response to our PF_KEY %s message for %s %s was of wrong type (%s)" , sparse_name(pfkey_type_names, pfkey_msg->sadb_msg_type) , description, text_said , sparse_val_show(pfkey_type_names, bp->msg.sadb_msg_type)); success = FALSE; } else if (response == NULL && bp->msg.sadb_msg_errno != 0) { /* KLIPS is signalling a problem */ loglog(RC_LOG_SERIOUS , "ERROR: PF_KEY %s response for %s %s included errno %u: %s" , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type) , description, text_said , (unsigned) bp->msg.sadb_msg_errno , strerror(bp->msg.sadb_msg_errno)); success = FALSE; } } } } /* all paths must exit this way to free resources */ pfkey_extensions_free(extensions); pfkey_msg_free(&pfkey_msg); return success; }
/* get a PF_KEY message from kernel. * Returns TRUE is message found, FALSE if no message pending, * and aborts or keeps trying when an error is encountered. * The only validation of the message is that the message length * received matches that in the message header, and that the message * is for this process. */ static bool pfkey_get(pfkey_buf *buf) { for (;;) { /* len must be less than PFKEYv2_MAX_MSGSIZE, * so it should fit in an int. We use this fact when printing it. */ ssize_t len; if (!pfkey_input_ready()) return FALSE; len = read(pfkeyfd, buf->bytes, sizeof(buf->bytes)); if (len < 0) { if (errno == EAGAIN) return FALSE; log_errno((e, "read() failed in pfkey_get()")); return FALSE; } else if ((size_t) len < sizeof(buf->msg)) { plog("pfkey_get read truncated PF_KEY message: %d bytes; ignoring message" , (int) len); } else if ((size_t) len != buf->msg.sadb_msg_len * IPSEC_PFKEYv2_ALIGN) { plog("pfkey_get read PF_KEY message with length %d that doesn't equal sadb_msg_len %u * %u; ignoring message" , (int) len , (unsigned) buf->msg.sadb_msg_len , (unsigned) IPSEC_PFKEYv2_ALIGN); } else if (!(buf->msg.sadb_msg_pid == (unsigned)pid /* for now, unsolicited messages can be: * SADB_ACQUIRE, SADB_REGISTER, SADB_X_NAT_T_NEW_MAPPING */ || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_ACQUIRE) #ifdef KERNEL_ALG || (buf->msg.sadb_msg_type == SADB_REGISTER) #endif #ifdef NAT_TRAVERSAL || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_X_NAT_T_NEW_MAPPING) #endif )) { /* not for us: ignore */ DBG(DBG_KLIPS, DBG_log("pfkey_get: ignoring PF_KEY %s message %u for process %u" , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) , buf->msg.sadb_msg_seq , buf->msg.sadb_msg_pid)); } else { DBG(DBG_KLIPS, DBG_log("pfkey_get: %s message %u" , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) , buf->msg.sadb_msg_seq)); return TRUE; } } }
static bool netlink_get(void) { struct { struct nlmsghdr n; char data[1024]; } rsp; ssize_t r; struct sockaddr_nl addr; socklen_t alen; alen = sizeof(addr); r = recvfrom(netlink_bcast_fd, &rsp, sizeof(rsp), 0 , (struct sockaddr *)&addr, &alen); if (r < 0) { if (errno == EAGAIN) return FALSE; if (errno != EINTR) log_errno((e, "recvfrom() failed in netlink_get")); return TRUE; } else if ((size_t) r < sizeof(rsp.n)) { openswan_log("netlink_get read truncated message: %ld bytes; ignore message" , (long) r); return TRUE; } else if (addr.nl_pid != 0) { /* not for us: ignore */ DBG(DBG_KLIPS, DBG_log("netlink_get: ignoring %s message from process %u" , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type) , addr.nl_pid)); return TRUE; } else if ((size_t) r != rsp.n.nlmsg_len) { openswan_log("netlink_get read message with length %ld that doesn't equal nlmsg_len %lu bytes; ignore message" , (long) r , (unsigned long) rsp.n.nlmsg_len); return TRUE; } DBG(DBG_KLIPS, DBG_log("netlink_get: %s message" , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type))); switch (rsp.n.nlmsg_type) { case XFRM_MSG_ACQUIRE: netlink_acquire(&rsp.n); break; case XFRM_MSG_POLEXPIRE: netlink_policy_expire(&rsp.n); break; default: /* ignored */ break; } return TRUE; }
/** send_netlink_msg * * @param hdr - Data to be sent. * @param rbuf - Return Buffer - contains data returned from the send. * @param rbuf_len - Length of rbuf * @param description - String - user friendly description of what is * being attempted. Used for diagnostics * @param text_said - String * @return bool True if the message was succesfully sent. */ static bool send_netlink_msg(struct nlmsghdr *hdr, struct nlmsghdr *rbuf, size_t rbuf_len , const char *description, const char *text_said) { struct { struct nlmsghdr n; struct nlmsgerr e; char data[1024]; } rsp; size_t len; ssize_t r; struct sockaddr_nl addr; static uint32_t seq; if (no_klips) { return TRUE; } hdr->nlmsg_seq = ++seq; len = hdr->nlmsg_len; do { r = write(netlinkfd, hdr, len); } while (r < 0 && errno == EINTR); if (r < 0) { log_errno((e , "netlink write() of %s message" " for %s %s failed" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , description, text_said)); return FALSE; } else if ((size_t)r != len) { loglog(RC_LOG_SERIOUS , "ERROR: netlink write() of %s message" " for %s %s truncated: %ld instead of %lu" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , description, text_said , (long)r, (unsigned long)len); return FALSE; } for (;;) { socklen_t alen; alen = sizeof(addr); r = recvfrom(netlinkfd, &rsp, sizeof(rsp), 0 , (struct sockaddr *)&addr, &alen); if (r < 0) { if (errno == EINTR) { continue; } log_errno((e , "netlink recvfrom() of response to our %s message" " for %s %s failed" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , description, text_said)); return FALSE; } else if ((size_t) r < sizeof(rsp.n)) { openswan_log("netlink read truncated message: %ld bytes; ignore message" , (long) r); continue; } else if (addr.nl_pid != 0) { /* not for us: ignore */ DBG(DBG_KLIPS, DBG_log("netlink: ignoring %s message from process %u" , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type) , addr.nl_pid)); continue; } else if (rsp.n.nlmsg_seq != seq) { DBG(DBG_KLIPS, DBG_log("netlink: ignoring out of sequence (%u/%u) message %s" , rsp.n.nlmsg_seq, seq , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type))); continue; } break; } if (rsp.n.nlmsg_len > (size_t) r) { loglog(RC_LOG_SERIOUS , "netlink recvfrom() of response to our %s message" " for %s %s was truncated: %ld instead of %lu" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , description, text_said , (long) len, (unsigned long) rsp.n.nlmsg_len); return FALSE; } else if (rsp.n.nlmsg_type != NLMSG_ERROR && (rbuf && rsp.n.nlmsg_type != rbuf->nlmsg_type)) { loglog(RC_LOG_SERIOUS , "netlink recvfrom() of response to our %s message" " for %s %s was of wrong type (%s)" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , description, text_said , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type)); return FALSE; } else if (rbuf) { if ((size_t) r > rbuf_len) { loglog(RC_LOG_SERIOUS , "netlink recvfrom() of response to our %s message" " for %s %s was too long: %ld > %lu" , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) , description, text_said , (long)r, (unsigned long)rbuf_len); return FALSE; } memcpy(rbuf, &rsp, r); return TRUE; } else if (rsp.n.nlmsg_type == NLMSG_ERROR && rsp.e.error) { loglog(RC_LOG_SERIOUS , "ERROR: netlink response for %s %s included errno %d: %s" , description, text_said , -rsp.e.error , strerror(-rsp.e.error)); return FALSE; } return TRUE; }