diff options
Diffstat (limited to 'src/libcharon/plugins/kernel_netlink')
4 files changed, 347 insertions, 44 deletions
| diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.in b/src/libcharon/plugins/kernel_netlink/Makefile.in index b2df52a61..26a7090b3 100644 --- a/src/libcharon/plugins/kernel_netlink/Makefile.in +++ b/src/libcharon/plugins/kernel_netlink/Makefile.in @@ -399,7 +399,6 @@ exec_prefix = @exec_prefix@  fips_mode = @fips_mode@  gtk_CFLAGS = @gtk_CFLAGS@  gtk_LIBS = @gtk_LIBS@ -h_plugins = @h_plugins@  host = @host@  host_alias = @host_alias@  host_cpu = @host_cpu@ @@ -434,6 +433,7 @@ nm_LIBS = @nm_LIBS@  nm_ca_dir = @nm_ca_dir@  nm_plugins = @nm_plugins@  oldincludedir = @oldincludedir@ +p_plugins = @p_plugins@  pcsclite_CFLAGS = @pcsclite_CFLAGS@  pcsclite_LIBS = @pcsclite_LIBS@  pdfdir = @pdfdir@ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index f3846ec07..becf6b5dc 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2006-2016 Tobias Brunner + * Copyright (C) 2006-2017 Tobias Brunner   * Copyright (C) 2005-2009 Martin Willi   * Copyright (C) 2008-2016 Andreas Steffen   * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser @@ -76,7 +76,7 @@  #endif  /** Base priority for installed policies */ -#define PRIO_BASE 100000 +#define PRIO_BASE 200000  /** Default lifetime of an acquire XFRM state (in seconds) */  #define DEFAULT_ACQUIRE_LIFETIME 165 @@ -117,7 +117,7 @@ struct kernel_algorithm_t {  	/**  	 * Name of the algorithm in linux crypto API  	 */ -	char *name; +	const char *name;  };  ENUM(xfrm_msg_names, XFRM_MSG_NEWSA, XFRM_MSG_MAPPING, @@ -221,6 +221,7 @@ static kernel_algorithm_t integrity_algs[] = {  /*	{AUTH_DES_MAC,				"***"				}, */  /*	{AUTH_KPDK_MD5,				"***"				}, */  	{AUTH_AES_XCBC_96,			"xcbc(aes)"			}, +	{AUTH_AES_CMAC_96,			"cmac(aes)"			},  };  /** @@ -236,7 +237,7 @@ static kernel_algorithm_t compression_algs[] = {  /**   * Look up a kernel algorithm name and its key size   */ -static char* lookup_algorithm(transform_type_t type, int ikev2) +static const char* lookup_algorithm(transform_type_t type, int ikev2)  {  	kernel_algorithm_t *list;  	int i, count; @@ -652,14 +653,15 @@ static inline uint32_t port_mask_bits(uint16_t port_mask)  /**   * Calculate the priority of a policy   * - * bits 0-0:  restriction to network interface (0..1)   1 bit - * bits 1-6:  src + dst port mask bits (2 * 0..16)      6 bits - * bits 7-7:  restriction to protocol (0..1)            1 bit - * bits 8-16: src + dst network mask bits (2 * 0..128)  9 bits - *                                                     17 bits + * bits 0-0:  separate trap and regular policies (0..1) 1 bit + * bits 1-1:  restriction to network interface (0..1)   1 bit + * bits 2-7:  src + dst port mask bits (2 * 0..16)      6 bits + * bits 8-8:  restriction to protocol (0..1)            1 bit + * bits 9-17: src + dst network mask bits (2 * 0..128)  9 bits + *                                                     18 bits   * - * smallest value: 000000000 0 000000 0:      0, lowest priority = 100'000 - * largest value : 100000000 1 100000 1: 65'729, highst priority =  34'271 + * smallest value: 000000000 0 000000 0 0:       0, lowest priority = 200'000 + * largest value : 100000000 1 100000 1 1: 131'459, highst priority =  68'541   */  static uint32_t get_priority(policy_entry_t *policy, policy_priority_t prio,  							 char *interface) @@ -672,8 +674,6 @@ static uint32_t get_priority(policy_entry_t *policy, policy_priority_t prio,  			priority += PRIO_BASE;  			/* fall-through to next case */  		case POLICY_PRIORITY_ROUTED: -			priority += PRIO_BASE; -			/* fall-through to next case */  		case POLICY_PRIORITY_DEFAULT:  			priority += PRIO_BASE;  			/* fall-through to next case */ @@ -684,10 +684,11 @@ static uint32_t get_priority(policy_entry_t *policy, policy_priority_t prio,  	dport_mask_bits = port_mask_bits(policy->sel.dport_mask);  	/* calculate priority */ -	priority -= (policy->sel.prefixlen_s + policy->sel.prefixlen_d) * 256; -	priority -=  policy->sel.proto ? 128 : 0; -	priority -= (sport_mask_bits + dport_mask_bits) * 2; -	priority -= (interface != NULL); +	priority -= (policy->sel.prefixlen_s + policy->sel.prefixlen_d) * 512; +	priority -=  policy->sel.proto ? 256 : 0; +	priority -= (sport_mask_bits + dport_mask_bits) * 4; +	priority -= (interface != NULL) * 2; +	priority -= (prio != POLICY_PRIORITY_ROUTED);  	return priority;  } @@ -1210,8 +1211,15 @@ METHOD(kernel_ipsec_t, get_spi, status_t,  	private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst,  	uint8_t protocol, uint32_t *spi)  { -	if (get_spi_internal(this, src, dst, protocol, -						 0xc0000000, 0xcFFFFFFF, spi) != SUCCESS) +	uint32_t spi_min, spi_max; + +	spi_min = lib->settings->get_int(lib->settings, "%s.spi_min", +									 KERNEL_SPI_MIN, lib->ns); +	spi_max = lib->settings->get_int(lib->settings, "%s.spi_max", +									 KERNEL_SPI_MAX, lib->ns); + +	if (get_spi_internal(this, src, dst, protocol, min(spi_min, spi_max), +						 max(spi_min, spi_max), spi) != SUCCESS)  	{  		DBG1(DBG_KNL, "unable to get SPI");  		return FAILED; @@ -1276,7 +1284,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,  	kernel_ipsec_add_sa_t *data)  {  	netlink_buf_t request; -	char *alg_name, markstr[32] = ""; +	const char *alg_name; +	char markstr[32] = "";  	struct nlmsghdr *hdr;  	struct xfrm_usersa_info *sa;  	uint16_t icv_size = 64, ipcomp = data->ipcomp; @@ -1367,6 +1376,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,  		default:  			break;  	} +	if (id->proto == IPPROTO_AH && sa->family == AF_INET) +	{	/* use alignment to 4 bytes for IPv4 instead of the incorrect 8 byte +		 * alignment that's used by default but is only valid for IPv6 */ +		sa->flags |= XFRM_STATE_ALIGN4; +	}  	sa->reqid = data->reqid;  	sa->lft.soft_byte_limit = XFRM_LIMIT(data->lifetime->bytes.rekey); @@ -2523,7 +2537,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,  	{	/* we don't update the policy if the priority is lower than that of  		 * the currently installed one */  		policy_change_done(this, policy); -		DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u," +		DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u, "  			 "refcount %d]", id->src_ts, id->dst_ts, policy_dir_names,  			 id->dir, markstr, cur_priority, use_count);  		return SUCCESS; diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index 0132f7269..2dc76d941 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -476,6 +476,11 @@ struct private_kernel_netlink_net_t {  	bool roam_events;  	/** +	 * whether to install IPsec policy routes +	 */ +	bool install_routes; + +	/**  	 * whether to actually install virtual IPs  	 */  	bool install_virtual_ip; @@ -795,6 +800,68 @@ static u_char get_scope(host_t *ip)  }  /** + * Determine the label of the given unicast IP address. + * + * We currently only support the default table given in RFC 6724: + * + *  Prefix        Precedence Label + *  ::1/128               50     0 + *  ::/0                  40     1 + *  ::ffff:0:0/96         35     4 + *  2002::/16             30     2 + *  2001::/32              5     5 + *  fc00::/7               3    13 + *  ::/96                  1     3 + *  fec0::/10              1    11 + *  3ffe::/16              1    12 + */ +static u_char get_label(host_t *ip) +{ +	struct { +		chunk_t net; +		u_char prefix; +		u_char label; +	} priorities[] = { +		/* priority table ordered by prefix */ +		/* ::1/128 */ +		{ chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01), 128, 0 }, +		/* ::ffff:0:0/96 */ +		{ chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00), 96, 4 }, +		/* ::/96 */ +		{ chunk_from_chars(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 96, 3 }, +		/* 2001::/32 */ +		{ chunk_from_chars(0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 32, 5 }, +		/* 2002::/16 */ +		{ chunk_from_chars(0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 16, 2 }, +		/* 3ffe::/16 */ +		{ chunk_from_chars(0x3f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 16, 12 }, +		/* fec0::/10 */ +		{ chunk_from_chars(0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 10, 11 }, +		/* fc00::/7 */ +		{ chunk_from_chars(0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +						   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), 7, 13 }, +	}; +	int i; + +	for (i = 0; i < countof(priorities); i++) +	{ +		if (host_in_subnet(ip, priorities[i].net, priorities[i].prefix)) +		{ +			return priorities[i].label; +		} +	} +	/* ::/0 */ +	return 1; +} + +/**   * Returns the length of the common prefix in bits up to the length of a's   * prefix, defined by RFC 6724 as the portion of the address not including the   * interface ID, which is 64-bit for most unicast addresses (see RFC 4291). @@ -829,7 +896,7 @@ static u_char common_prefix(host_t *a, host_t *b)  static bool is_address_better(private_kernel_netlink_net_t *this,  							  addr_entry_t *a, addr_entry_t *b, host_t *d)  { -	u_char sa, sb, sd, pa, pb; +	u_char sa, sb, sd, la, lb, ld, pa, pb;  	/* rule 2: prefer appropriate scope */  	if (d) @@ -858,9 +925,22 @@ static bool is_address_better(private_kernel_netlink_net_t *this,  	/* rule 4 is not applicable as we don't know if an address is a home or  	 * care-of addresses.  	 * rule 5 does not apply as we only compare addresses from one interface -	 * rule 6 requires a policy table (optionally configurable) to match -	 * configurable labels  	 */ +	/* rule 6: prefer matching label */ +	if (d) +	{ +		la = get_label(a->ip); +		lb = get_label(b->ip); +		ld = get_label(d); +		if (la == ld && lb != ld) +		{ +			return FALSE; +		} +		else if (lb == ld && la != ld) +		{ +			return TRUE; +		} +	}  	/* rule 7: prefer temporary addresses (WE REVERSE THIS BY DEFAULT!) */  	if ((a->flags & IFA_F_TEMPORARY) != (b->flags & IFA_F_TEMPORARY))  	{ @@ -1795,12 +1875,22 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,  	{	/* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes.  		 * as we want to ignore routes with virtual IPs we cannot use DUMP  		 * if these routes are not installed in a separate table */ -		hdr->nlmsg_flags |= NLM_F_DUMP; +		if (this->install_routes) +		{ +			hdr->nlmsg_flags |= NLM_F_DUMP; +		}  	}  	if (candidate)  	{  		chunk = candidate->get_address(candidate); -		netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); +		if (hdr->nlmsg_flags & NLM_F_DUMP) +		{ +			netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); +		} +		else +		{ +			netlink_add_attribute(hdr, RTA_SRC, chunk, sizeof(request)); +		}  	}  	/* we use this below to match against the routes */  	chunk = dest->get_address(dest); @@ -2050,6 +2140,146 @@ METHOD(kernel_net_t, get_nexthop, host_t*,  	return get_route(this, dest, prefix, TRUE, src, iface, 0);  } +/** enumerator over subnets */ +typedef struct { +	enumerator_t public; +	private_kernel_netlink_net_t *private; +	/** message from the kernel */ +	struct nlmsghdr *msg; +	/** current message from the kernel */ +	struct nlmsghdr *current; +	/** remaining length */ +	size_t len; +	/** last subnet enumerated */ +	host_t *net; +	/** interface of current net */ +	char ifname[IFNAMSIZ]; +} subnet_enumerator_t; + +METHOD(enumerator_t, destroy_subnet_enumerator, void, +	subnet_enumerator_t *this) +{ +	DESTROY_IF(this->net); +	free(this->msg); +	free(this); +} + +METHOD(enumerator_t, enumerate_subnets, bool, +	subnet_enumerator_t *this, host_t **net, uint8_t *mask, char **ifname) +{ +	if (!this->current) +	{ +		this->current = this->msg; +	} +	else +	{ +		this->current = NLMSG_NEXT(this->current, this->len); +		DESTROY_IF(this->net); +		this->net = NULL; +	} + +	while (NLMSG_OK(this->current, this->len)) +	{ +		switch (this->current->nlmsg_type) +		{ +			case NLMSG_DONE: +				break; +			case RTM_NEWROUTE: +			{ +				struct rtmsg *msg; +				struct rtattr *rta; +				size_t rtasize; +				chunk_t dst = chunk_empty; +				uint32_t oif = 0; + +				msg = NLMSG_DATA(this->current); + +				if (!route_usable(this->current)) +				{ +					break; +				} +				else if (msg->rtm_table && ( +							msg->rtm_table == RT_TABLE_LOCAL || +							msg->rtm_table == this->private->routing_table)) +				{	/* ignore our own and the local routing tables */ +					break; +				} + +				rta = RTM_RTA(msg); +				rtasize = RTM_PAYLOAD(this->current); +				while (RTA_OK(rta, rtasize)) +				{ +					switch (rta->rta_type) +					{ +						case RTA_DST: +							dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); +							break; +						case RTA_OIF: +							if (RTA_PAYLOAD(rta) == sizeof(oif)) +							{ +								oif = *(uint32_t*)RTA_DATA(rta); +							} +							break; +					} +					rta = RTA_NEXT(rta, rtasize); +				} + +				if (dst.ptr && oif && if_indextoname(oif, this->ifname)) +				{ +					this->net = host_create_from_chunk(msg->rtm_family, dst, 0); +					*net = this->net; +					*mask = msg->rtm_dst_len; +					*ifname = this->ifname; +					return TRUE; +				} +				break; +			} +			default: +				break; +		} +		this->current = NLMSG_NEXT(this->current, this->len); +	} +	return FALSE; +} + +METHOD(kernel_net_t, create_local_subnet_enumerator, enumerator_t*, +	private_kernel_netlink_net_t *this) +{ +	netlink_buf_t request; +	struct nlmsghdr *hdr, *out; +	struct rtmsg *msg; +	size_t len; +	subnet_enumerator_t *enumerator; + +	memset(&request, 0, sizeof(request)); + +	hdr = &request.hdr; +	hdr->nlmsg_flags = NLM_F_REQUEST; +	hdr->nlmsg_type = RTM_GETROUTE; +	hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); +	hdr->nlmsg_flags |= NLM_F_DUMP; + +	msg = NLMSG_DATA(hdr); +	msg->rtm_scope = RT_SCOPE_LINK; + +	if (this->socket->send(this->socket, hdr, &out, &len) != SUCCESS) +	{ +		DBG2(DBG_KNL, "enumerating local subnets failed"); +		return enumerator_create_empty(); +	} + +	INIT(enumerator, +		.public = { +			.enumerate = (void*)_enumerate_subnets, +			.destroy = _destroy_subnet_enumerator, +		}, +		.private = this, +		.msg = out, +		.len = len, +	); +	return &enumerator->public; +} +  /**   * Manages the creation and deletion of ip addresses on an interface.   * By setting the appropriate nlmsg_type, the ip will be set or unset. @@ -2080,16 +2310,22 @@ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type  	netlink_add_attribute(hdr, IFA_LOCAL, chunk, sizeof(request)); -	if (ip->get_family(ip) == AF_INET6 && this->rta_prefsrc_for_ipv6) -	{	/* if source routes are possible we let the virtual IP get deprecated -		 * immediately (but mark it as valid forever) so it gets only used if -		 * forced by our route, and not by the default IPv6 address selection */ -		struct ifa_cacheinfo cache = { -			.ifa_valid = 0xFFFFFFFF, -			.ifa_prefered = 0, -		}; -		netlink_add_attribute(hdr, IFA_CACHEINFO, chunk_from_thing(cache), -							  sizeof(request)); +	if (ip->get_family(ip) == AF_INET6) +	{ +		msg->ifa_flags |= IFA_F_NODAD; +		if (this->rta_prefsrc_for_ipv6) +		{ +			/* if source routes are possible we let the virtual IP get +			 * deprecated immediately (but mark it as valid forever) so it gets +			 * only used if forced by our route, and not by the default IPv6 +			 * address selection */ +			struct ifa_cacheinfo cache = { +				.ifa_valid = 0xFFFFFFFF, +				.ifa_prefered = 0, +			}; +			netlink_add_attribute(hdr, IFA_CACHEINFO, chunk_from_thing(cache), +								  sizeof(request)); +		}  	}  	return this->socket->send_ack(this->socket, hdr);  } @@ -2680,6 +2916,7 @@ kernel_netlink_net_t *kernel_netlink_net_create()  			.interface = {  				.get_interface = _get_interface_name,  				.create_address_enumerator = _create_address_enumerator, +				.create_local_subnet_enumerator = _create_local_subnet_enumerator,  				.get_source_addr = _get_source_addr,  				.get_nexthop = _get_nexthop,  				.add_ip = _add_ip, @@ -2715,6 +2952,8 @@ kernel_netlink_net_t *kernel_netlink_net_create()  						"%s.routing_table_prio", ROUTING_TABLE_PRIO, lib->ns),  		.process_route = lib->settings->get_bool(lib->settings,  						"%s.process_route", TRUE, lib->ns), +		.install_routes = lib->settings->get_bool(lib->settings, +						"%s.install_routes", TRUE, lib->ns),  		.install_virtual_ip = lib->settings->get_bool(lib->settings,  						"%s.install_virtual_ip", TRUE, lib->ns),  		.install_virtual_ip_on = lib->settings->get_str(lib->settings, diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index 7165b655b..da54031a1 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -15,6 +15,29 @@   * for more details.   */ +/* + * Copyright (C) 2016 secunet Security Networks AG + * Copyright (C) 2016 Thomas Egerer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 <sys/socket.h>  #include <linux/netlink.h>  #include <linux/rtnetlink.h> @@ -281,8 +304,9 @@ static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in,  						  uintptr_t seq, struct nlmsghdr **out, size_t *out_len)  {  	struct nlmsghdr *hdr; -	chunk_t result = {};  	entry_t *entry; +	u_char *ptr; +	int i;  	in->nlmsg_seq = seq;  	in->nlmsg_pid = getpid(); @@ -343,6 +367,14 @@ static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in,  		return OUT_OF_RES;  	} +	for (i = 0, *out_len = 0; i < array_count(entry->hdrs); i++) +	{ +		array_get(entry->hdrs, i, &hdr); +		*out_len += hdr->nlmsg_len; +	} +	ptr = malloc(*out_len); +	*out = (struct nlmsghdr*)ptr; +  	while (array_remove(entry->hdrs, ARRAY_HEAD, &hdr))  	{  		if (this->names) @@ -350,14 +382,11 @@ static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in,  			DBG3(DBG_KNL, "received %N %u: %b", this->names, hdr->nlmsg_type,  				 hdr->nlmsg_seq, hdr, hdr->nlmsg_len);  		} -		result = chunk_cat("mm", result, -						   chunk_create((char*)hdr, hdr->nlmsg_len)); +		memcpy(ptr, hdr, hdr->nlmsg_len); +		ptr += hdr->nlmsg_len; +		free(hdr);  	}  	destroy_entry(entry); - -	*out_len = result.len; -	*out = (struct nlmsghdr*)result.ptr; -  	return SUCCESS;  } @@ -557,6 +586,8 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,  	struct sockaddr_nl addr = {  		.nl_family = AF_NETLINK,  	}; +	bool force_buf = FALSE; +	int rcvbuf_size = 0;  	INIT(this,  		.public = { @@ -606,6 +637,25 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,  		destroy(this);  		return NULL;  	} +	rcvbuf_size = lib->settings->get_int(lib->settings, +						"%s.plugins.kernel-netlink.receive_buffer_size", +						rcvbuf_size, lib->ns); +	if (rcvbuf_size) +	{ +		int optname; + +		force_buf = lib->settings->get_bool(lib->settings, +						"%s.plugins.kernel-netlink.force_receive_buffer_size", +						force_buf, lib->ns); +		optname = force_buf ? SO_RCVBUFFORCE : SO_RCVBUF; + +		if (setsockopt(this->socket, SOL_SOCKET, optname, &rcvbuf_size, +					   sizeof(rcvbuf_size)) == -1) +		{ +			DBG1(DBG_KNL, "failed to %supdate receive buffer size to %d: %s", +					force_buf ? "forcibly " : "", rcvbuf_size, strerror(errno)); +		} +	}  	if (this->parallel)  	{  		lib->watcher->add(lib->watcher, this->socket, WATCHER_READ, watch, this); | 
