📄 kernel_netlink.c
字号:
/* netlink interface to the kernel's IPsec mechanism * Copyright (C) 2003 Herbert Xu. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: kernel_netlink.c,v 1.20 2004/09/07 19:00:15 ken Exp $ */#if defined(linux) && defined(KERNEL26_SUPPORT)#include <errno.h>#include <fcntl.h>#include <string.h>#include <sys/queue.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/queue.h>#include <unistd.h>#include "kameipsec.h"#include <rtnetlink.h>#include <xfrm.h>#include <openswan.h>#include <pfkeyv2.h>#include <pfkey.h>#include "constants.h"#include "defs.h"#include "id.h"#include "connections.h"#include "kernel.h"#include "kernel_netlink.h"#include "kernel_pfkey.h"#include "log.h"#include "whack.h" /* for RC_LOG_SERIOUS */#include "kernel_alg.h"#ifdef XAUTH_USEPAM#include <security/pam_appl.h>#endif/* Minimum priority number in SPD used by pluto. */#define MIN_SPD_PRIORITY 1024static int netlinkfd = NULL_FD;static int netlink_bcast_fd = NULL_FD;#define NE(x) { x, #x } /* Name Entry -- shorthand for sparse_names */static sparse_names xfrm_type_names = { NE(NLMSG_NOOP), NE(NLMSG_ERROR), NE(NLMSG_DONE), NE(NLMSG_OVERRUN), NE(XFRM_MSG_NEWSA), NE(XFRM_MSG_DELSA), NE(XFRM_MSG_GETSA), NE(XFRM_MSG_NEWPOLICY), NE(XFRM_MSG_DELPOLICY), NE(XFRM_MSG_GETPOLICY), NE(XFRM_MSG_ALLOCSPI), NE(XFRM_MSG_ACQUIRE), NE(XFRM_MSG_EXPIRE), NE(XFRM_MSG_UPDPOLICY), NE(XFRM_MSG_UPDSA), NE(XFRM_MSG_POLEXPIRE), NE(XFRM_MSG_MAX), { 0, sparse_end }};#undef NE/** Authentication Algs */static sparse_names aalg_list = { { SADB_X_AALG_NULL, "digest_null" }, { SADB_AALG_MD5HMAC, "md5" }, { SADB_AALG_SHA1HMAC, "sha1" }, { SADB_X_AALG_SHA2_256HMAC, "sha256" }, { SADB_X_AALG_RIPEMD160HMAC, "ripemd160" }, { 0, sparse_end }};/** Encryption algs */static sparse_names ealg_list = { { SADB_EALG_NULL, "cipher_null" }, { SADB_EALG_DESCBC, "des" }, { SADB_EALG_3DESCBC, "des3_ede" }, { SADB_X_EALG_CASTCBC, "cast128" }, { SADB_X_EALG_BLOWFISHCBC, "blowfish" }, { SADB_X_EALG_AESCBC, "aes" }, { 0, sparse_end }};/** Compress Algs */static sparse_names calg_list = { { SADB_X_CALG_DEFLATE, "deflate" }, { SADB_X_CALG_LZS, "lzs" }, { SADB_X_CALG_LZJH, "lzjh" }, { 0, sparse_end }};/** ip2xfrm - Take an IP address and convert to an xfrm. * * @param addr ip_address * @param xaddr xfrm_address_t - IPv[46] Address from addr is copied here. */static void ip2xfrm(const ip_address *addr, xfrm_address_t *xaddr){ /* If it's an IPv4 address */ if (addr->u.v4.sin_family == AF_INET) { xaddr->a4 = addr->u.v4.sin_addr.s_addr; } else /* Must be IPv6 */ { memcpy(xaddr->a6, &addr->u.v6.sin6_addr, sizeof(xaddr->a6)); }}/** init_netlink - Initialize the netlink inferface. Opens the sockets and * then binds to the broadcast socket. */static void init_netlink(void){ struct sockaddr_nl addr; netlinkfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM); if (netlinkfd < 0) exit_log_errno((e, "socket() in init_netlink()")); if (fcntl(netlinkfd, F_SETFD, FD_CLOEXEC) != 0) exit_log_errno((e, "fcntl(FD_CLOEXEC) in init_netlink()")); netlink_bcast_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM); if (netlink_bcast_fd < 0) exit_log_errno((e, "socket() for bcast in init_netlink()")); if (fcntl(netlink_bcast_fd, F_SETFD, FD_CLOEXEC) != 0) exit_log_errno((e, "fcntl(FD_CLOEXEC) for bcast in init_netlink()")); if (fcntl(netlink_bcast_fd, F_SETFL, O_NONBLOCK) != 0) exit_log_errno((e, "fcntl(O_NONBLOCK) for bcast in init_netlink()")); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; if (bind(netlink_bcast_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) exit_log_errno((e, "Failed to bind bcast socket in init_netlink()"));}/** 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 boolsend_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;}/** netlink_policy - * * @param hdr - Data to check * @param enoent_ok - Boolean - OK or not OK. * @param text_said - String * @return boolean */static boolnetlink_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;}/** netlink_raw_eroute * * @param this_host ip_address * @param this_client ip_subnet * @param that_host ip_address * @param that_client ip_subnet * @param spi * @param proto int (Currently unused) Contains protocol (u=tcp, 17=udp, etc...) * @param transport_proto int (Currently unused) 0=tunnel, 1=transport * @param satype int * @param proto_info * @param lifetime (Currently unused) * @param ip int * @return boolean True if successful */static boolnetlink_raw_eroute(const ip_address *this_host , const ip_subnet *this_client , const ip_address *that_host , const ip_subnet *that_client , ipsec_spi_t spi , unsigned int proto UNUSED , unsigned int transport_proto UNUSED , unsigned int satype , const struct pfkey_proto_info *proto_info , time_t use_lifetime UNUSED , unsigned int op , const char *text_said){ struct { struct nlmsghdr n; union { struct xfrm_userpolicy_info p; struct xfrm_userpolicy_id id; } u; char data[1024]; } req; int shift; int dir; int family; int policy; bool ok; bool enoent_ok; policy = IPSEC_POLICY_IPSEC; if (satype == SADB_X_SATYPE_INT) { /* shunt route */ switch (ntohl(spi)) { case SPI_PASS: policy = IPSEC_POLICY_NONE; break; case SPI_DROP: case SPI_REJECT: default: policy = IPSEC_POLICY_DISCARD; break; case SPI_TRAP: case SPI_TRAPSUBNET: case SPI_HOLD: if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) { return TRUE; } break; } } memset(&req, 0, sizeof(req)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; family = that_client->addr.u.v4.sin_family; shift = (family == AF_INET) ? 5 : 7; req.u.p.sel.sport = portof(&this_client->addr); req.u.p.sel.dport = portof(&that_client->addr); req.u.p.sel.sport_mask = (req.u.p.sel.sport) ? ~0:0; req.u.p.sel.dport_mask = (req.u.p.sel.dport) ? ~0:0; ip2xfrm(&this_client->addr, &req.u.p.sel.saddr); ip2xfrm(&that_client->addr, &req.u.p.sel.daddr); req.u.p.sel.prefixlen_s = this_client->maskbits; req.u.p.sel.prefixlen_d = that_client->maskbits; req.u.p.sel.proto = transport_proto; req.u.p.sel.family = family; dir = XFRM_POLICY_OUT; if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) { dir = XFRM_POLICY_IN; } if ((op & ERO_MASK) == ERO_DELETE) { req.u.id.dir = dir; req.n.nlmsg_type = XFRM_MSG_DELPOLICY; req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.u.id))); } else { int src, dst; req.u.p.dir = dir; src = req.u.p.sel.prefixlen_s; dst = req.u.p.sel.prefixlen_d; if (dir != XFRM_POLICY_OUT) { src = req.u.p.sel.prefixlen_d; dst = req.u.p.sel.prefixlen_s; } req.u.p.priority = MIN_SPD_PRIORITY + (((2 << shift) - src) << shift) + (2 << shift) - dst; req.u.p.action = XFRM_POLICY_ALLOW; if (policy == IPSEC_POLICY_DISCARD) { req.u.p.action = XFRM_POLICY_BLOCK; } req.u.p.lft.soft_use_expires_seconds = use_lifetime; req.u.p.lft.soft_byte_limit = XFRM_INF; req.u.p.lft.soft_packet_limit = XFRM_INF; req.u.p.lft.hard_byte_limit = XFRM_INF; req.u.p.lft.hard_packet_limit = XFRM_INF; req.n.nlmsg_type = XFRM_MSG_NEWPOLICY; if (op & (SADB_X_SAFLAGS_REPLACEFLOW << ERO_FLAG_SHIFT)) { req.n.nlmsg_type = XFRM_MSG_UPDPOLICY; } req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.u.p))); } if (policy == IPSEC_POLICY_IPSEC && (op & ERO_MASK) != ERO_DELETE) { struct rtattr *attr; struct xfrm_user_tmpl tmpl[4]; int i; memset(tmpl, 0, sizeof(tmpl)); for (i = 0; proto_info[i].proto; i++) { tmpl[i].reqid = proto_info[i].reqid; tmpl[i].id.proto = proto_info[i].proto; tmpl[i].optional = proto_info[i].proto == IPPROTO_COMP; tmpl[i].aalgos = tmpl[i].ealgos = tmpl[i].calgos = ~0; tmpl[i].mode = proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; if (!tmpl[i].mode) { continue; } ip2xfrm(this_host, &tmpl[i].saddr); ip2xfrm(that_host, &tmpl[i].id.daddr); } attr = (struct rtattr *)((char *)&req + req.n.nlmsg_len); attr->rta_type = XFRMA_TMPL; attr->rta_len = i * sizeof(tmpl[0]); memcpy(RTA_DATA(attr), tmpl, attr->rta_len); attr->rta_len = RTA_LENGTH(attr->rta_len); req.n.nlmsg_len += attr->rta_len; } enoent_ok = FALSE; if (op == ERO_DEL_INBOUND) { enoent_ok = TRUE; } else if (op == ERO_DELETE && ntohl(spi) == SPI_HOLD) { enoent_ok = TRUE; } ok = netlink_policy(&req.n, enoent_ok, text_said); switch (dir) { case XFRM_POLICY_IN: if (req.n.nlmsg_type == XFRM_MSG_DELPOLICY) { req.u.id.dir = XFRM_POLICY_FWD; } else if (!ok)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -