netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH iproute2-next v2] ip fou: Support binding FOU ports
@ 2019-04-18 12:19 Kristian Evensen
  2019-04-21  1:33 ` David Ahern
  0 siblings, 1 reply; 4+ messages in thread
From: Kristian Evensen @ 2019-04-18 12:19 UTC (permalink / raw)
  To: netdev; +Cc: Kristian Evensen

This patch adds support for binding FOU ports using iproute2.
Kernel-support was added in 1713cb37bf67 ("fou: Support binding FoU
socket").

The parse function now handles new arguments for setting the
binding-related attributes, while the print function writes the new
attributes if they are set. Also, the man page has been updated.

v1->v2 (all changes suggested by David Ahren):
* Fix reverse Christmas tree ordering.
* Remove redundant peer_port_set-variable, it is enough to check
peer_port.
* Add proper error handling of invalid local/peer addresses.
* Use interface name and not index.
* Remove updating fou-header file, it is already done.

Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
---
 ip/ipfou.c        | 137 +++++++++++++++++++++++++++++++++++++++++++---
 man/man8/ip-fou.8 |  49 ++++++++++++++++-
 2 files changed, 177 insertions(+), 9 deletions(-)

diff --git a/ip/ipfou.c b/ip/ipfou.c
index 346522dd..6ea20d3b 100644
--- a/ip/ipfou.c
+++ b/ip/ipfou.c
@@ -28,11 +28,16 @@ static void usage(void)
 {
 	fprintf(stderr,
 		"Usage: ip fou add port PORT { ipproto PROTO  | gue } [ -6 ]\n"
-		"       ip fou del port PORT [ -6 ]\n"
+		"		   [ local IFADDR ] [ peer IFADDR ]\n"
+		"		   [ peer_port PORT ] [ dev IFNAME ]\n"
+		"       ip fou del port PORT [ -6 ] [ local IFADDR ]\n"
+		"		   [ peer IFADDR ] [ peer_port PORT ]\n"
+		"		   [ dev IFNAME ]\n"
 		"       ip fou show\n"
 		"\n"
 		"Where: PROTO { ipproto-name | 1..255 }\n"
-		"       PORT { 1..65535 }\n");
+		"       PORT { 1..65535 }\n"
+		"       IFADDR { addr }\n");
 
 	exit(-1);
 }
@@ -48,12 +53,14 @@ static int genl_family = -1;
 static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
 			 bool adding)
 {
-	__u16 port;
-	int port_set = 0;
-	__u8 ipproto, type;
+	const char *local = NULL, *peer = NULL;
+	__u16 port, peer_port = 0;
+	__u8 family = AF_INET;
 	bool gue_set = false;
 	int ipproto_set = 0;
-	__u8 family = AF_INET;
+	__u8 ipproto, type;
+	int port_set = 0;
+	int index = 0;
 
 	while (argc > 0) {
 		if (!matches(*argv, "port")) {
@@ -77,6 +84,38 @@ static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
 			gue_set = true;
 		} else if (!matches(*argv, "-6")) {
 			family = AF_INET6;
+		} else if (!matches(*argv, "local")) {
+			NEXT_ARG();
+
+			local = *argv;
+		} else if (!matches(*argv, "peer")) {
+			NEXT_ARG();
+
+			peer = *argv;
+		} else if (!matches(*argv, "peer_port")) {
+			NEXT_ARG();
+
+			if (get_be16(&peer_port, *argv, 0) || peer_port == 0)
+				invarg("invalid peer port", *argv);
+		} else if (!matches(*argv, "dev")) {
+			const char *ifname;
+
+			NEXT_ARG();
+
+			ifname = *argv;
+
+			if (check_ifname(ifname)) {
+				fprintf(stderr, "fou: invalid device name\n");
+				exit(EXIT_FAILURE);
+			}
+
+			ll_init_map(&rth);
+			index = ll_name_to_index(ifname);
+
+			if (!index) {
+				fprintf(stderr, "fou: unknown device name\n");
+				exit(EXIT_FAILURE);
+			}
 		} else {
 			fprintf(stderr
 				, "fou: unknown command \"%s\"?\n", *argv);
@@ -101,6 +140,11 @@ static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
 		return -1;
 	}
 
+	if ((peer_port && !peer) || (peer && !peer_port)) {
+		fprintf(stderr, "fou: both peer and peer port must be set\n");
+		return -1;
+	}
+
 	type = gue_set ? FOU_ENCAP_GUE : FOU_ENCAP_DIRECT;
 
 	addattr16(n, 1024, FOU_ATTR_PORT, port);
@@ -110,6 +154,38 @@ static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
 	if (ipproto_set)
 		addattr8(n, 1024, FOU_ATTR_IPPROTO, ipproto);
 
+	if (local) {
+		inet_prefix local_addr;
+		__u8 attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 :
+						     FOU_ATTR_LOCAL_V6;
+
+		if (get_addr(&local_addr, local, family)) {
+			fprintf(stderr, "fou: parsing local address failed\n");
+			exit(EXIT_FAILURE);
+		}
+		addattr_l(n, 1024, attr_type, &local_addr.data,
+			  local_addr.bytelen);
+	}
+
+	if (peer) {
+		inet_prefix peer_addr;
+		__u8 attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 :
+						     FOU_ATTR_PEER_V6;
+
+		if (get_addr(&peer_addr, peer, family)) {
+			fprintf(stderr, "fou: parsing peer address failed\n");
+			exit(EXIT_FAILURE);
+		}
+		addattr_l(n, 1024, attr_type, &peer_addr.data,
+			  peer_addr.bytelen);
+
+		if (peer_port)
+			addattr16(n, 1024, FOU_ATTR_PEER_PORT, peer_port);
+	}
+
+	if (index)
+		addattr32(n, 1024, FOU_ATTR_IFINDEX, index);
+
 	return 0;
 }
 
@@ -139,8 +215,10 @@ static int do_del(int argc, char **argv)
 
 static int print_fou_mapping(struct nlmsghdr *n, void *arg)
 {
-	struct genlmsghdr *ghdr;
+	__u8 family = AF_INET, local_attr_type, peer_attr_type, byte_len;
 	struct rtattr *tb[FOU_ATTR_MAX + 1];
+	__u8 empty_buf[16] = {0};
+	struct genlmsghdr *ghdr;
 	int len = n->nlmsg_len;
 
 	if (n->nlmsg_type != genl_family)
@@ -166,7 +244,7 @@ static int print_fou_mapping(struct nlmsghdr *n, void *arg)
 			   " ipproto %u", rta_getattr_u8(tb[FOU_ATTR_IPPROTO]));
 
 	if (tb[FOU_ATTR_AF]) {
-		__u8 family = rta_getattr_u8(tb[FOU_ATTR_AF]);
+		family = rta_getattr_u8(tb[FOU_ATTR_AF]);
 
 		print_string(PRINT_JSON, "family", NULL,
 			     family_name(family));
@@ -175,6 +253,49 @@ static int print_fou_mapping(struct nlmsghdr *n, void *arg)
 			print_string(PRINT_FP, NULL,
 				     " -6", NULL);
 	}
+
+	local_attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 :
+					      FOU_ATTR_LOCAL_V6;
+	peer_attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 :
+					     FOU_ATTR_PEER_V6;
+	byte_len = af_bit_len(family) / 8;
+
+	if (tb[local_attr_type] && memcmp(RTA_DATA(tb[local_attr_type]),
+					  empty_buf, byte_len)) {
+		print_string(PRINT_ANY, "local", " local %s",
+			     format_host_rta(family, tb[local_attr_type]));
+	}
+
+	if (tb[peer_attr_type] && memcmp(RTA_DATA(tb[peer_attr_type]),
+					 empty_buf, byte_len)) {
+		print_string(PRINT_ANY, "peer", " peer %s",
+			     format_host_rta(family, tb[peer_attr_type]));
+	}
+
+	if (tb[FOU_ATTR_PEER_PORT]) {
+		__u16 p_port = ntohs(rta_getattr_u16(tb[FOU_ATTR_PEER_PORT]));
+
+		if (p_port)
+			print_uint(PRINT_ANY, "peer_port", " peer_port %u",
+				   p_port);
+
+	}
+
+	if (tb[FOU_ATTR_IFINDEX]) {
+		int index = rta_getattr_s32(tb[FOU_ATTR_IFINDEX]);
+
+		if (index) {
+			const char *ifname;
+
+			ll_init_map(&rth);
+			ifname = ll_index_to_name(index);
+
+			if (ifname)
+				print_string(PRINT_ANY, "dev", " dev %s",
+					     ifname);
+		}
+	}
+
 	print_string(PRINT_FP, NULL, "\n", NULL);
 	close_json_object();
 
diff --git a/man/man8/ip-fou.8 b/man/man8/ip-fou.8
index 81cab928..f4e08f16 100644
--- a/man/man8/ip-fou.8
+++ b/man/man8/ip-fou.8
@@ -24,11 +24,43 @@ ip-gue \- Generic UDP Encapsulation receive port configuration
 .B ipproto
 .IR PROTO
 .RB " }"
+.RB "[ "
+.B local
+.IR IFADDR
+.RB " ]"
+.RB "[ "
+.B peer
+.IR IFADDR
+.RB " ]"
+.RB "[ "
+.B peer_port
+.IR PORT
+.RB " ]"
+.RB "[ "
+.B dev
+.IR IFNAME
+.RB " ]"
 .br
 .ti -8
 .BR "ip fou del"
 .B port
 .IR PORT
+.RB "[ "
+.B local
+.IR IFADDR
+.RB " ]"
+.RB "[ "
+.B peer
+.IR IFADDR
+.RB " ]"
+.RB "[ "
+.B peer_port
+.IR PORT
+.RB " ]"
+.RB "[ "
+.B dev
+.IR IFNAME
+.RB " ]"
 .br
 .ti -8
 .B ip fou show
@@ -50,11 +82,22 @@ When creating a FOU or GUE receive port, the port number is specified in
 .I PORT
 argument. If FOU is used, the IP protocol number associated with the port is specified in
 .I PROTO
+argument. You can bind a port to a local address/interface, by specifying the
+address in the local
+.I IFADDR
+argument or the device in the
+.I IFNAME
+argument. If you would like to connect the port, you can specify the peer
+address in the peer
+.I IFADDR
+argument and peer port in the peer_port
+.I PORT
 argument.
 .PP
 A FOU or GUE receive port is deleted by specifying
 .I PORT
-in the delete command.
+in the delete command, as well as local address/interface or peer address/port
+(if set).
 .SH EXAMPLES
 .PP
 .SS Configure a FOU receive port for GRE bound to 7777
@@ -72,6 +115,10 @@ in the delete command.
 .SS Delete the GUE receive port bound to 9999
 .nf
 # ip fou del port 9999
+.SS Configure a FOU receive port for GRE bound to 1.2.3.4:7777
+.nf
+# ip fou add port 7777 ipproto 47 local 1.2.3.4
+.PP
 .SH SEE ALSO
 .br
 .BR ip (8)
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH iproute2-next v2] ip fou: Support binding FOU ports
  2019-04-18 12:19 [PATCH iproute2-next v2] ip fou: Support binding FOU ports Kristian Evensen
@ 2019-04-21  1:33 ` David Ahern
  2019-04-22 11:29   ` Kristian Evensen
  0 siblings, 1 reply; 4+ messages in thread
From: David Ahern @ 2019-04-21  1:33 UTC (permalink / raw)
  To: Kristian Evensen, netdev

On 4/18/19 6:19 AM, Kristian Evensen wrote:
> @@ -77,6 +84,38 @@ static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
>  			gue_set = true;
>  		} else if (!matches(*argv, "-6")) {
>  			family = AF_INET6;
> +		} else if (!matches(*argv, "local")) {
> +			NEXT_ARG();
> +
> +			local = *argv;
> +		} else if (!matches(*argv, "peer")) {
> +			NEXT_ARG();
> +
> +			peer = *argv;
> +		} else if (!matches(*argv, "peer_port")) {
> +			NEXT_ARG();
> +
> +			if (get_be16(&peer_port, *argv, 0) || peer_port == 0)
> +				invarg("invalid peer port", *argv);
> +		} else if (!matches(*argv, "dev")) {
> +			const char *ifname;
> +
> +			NEXT_ARG();
> +
> +			ifname = *argv;
> +
> +			if (check_ifname(ifname)) {
> +				fprintf(stderr, "fou: invalid device name\n");
> +				exit(EXIT_FAILURE);
> +			}
> +
> +			ll_init_map(&rth);

Missed this in v1: you definitely do not want to call ll_init_map here.
It does a full kink dump which can be expensive on large scale setups.
ll_name_to_index alone should be fine.

> +			index = ll_name_to_index(ifname);
> +
> +			if (!index) {
> +				fprintf(stderr, "fou: unknown device name\n");
> +				exit(EXIT_FAILURE);
> +			}
>  		} else {
>  			fprintf(stderr
>  				, "fou: unknown command \"%s\"?\n", *argv);

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH iproute2-next v2] ip fou: Support binding FOU ports
  2019-04-21  1:33 ` David Ahern
@ 2019-04-22 11:29   ` Kristian Evensen
  2019-04-22 14:46     ` David Ahern
  0 siblings, 1 reply; 4+ messages in thread
From: Kristian Evensen @ 2019-04-22 11:29 UTC (permalink / raw)
  To: David Ahern; +Cc: Network Development

Hi David,

On Sun, Apr 21, 2019 at 3:33 AM David Ahern <dsahern@gmail.com> wrote:
> Missed this in v1: you definitely do not want to call ll_init_map here.
> It does a full kink dump which can be expensive on large scale setups.
> ll_name_to_index alone should be fine.

Thanks a lot for all your comments! It is not so strange that you
missed the ll_init_map()-call in v1, as it was not there :) I have
removed the call you commented and fou_parse_opt() works as intended.
I initially thought that ll_init_map() was required for the different
lookup-functions to work, but I see now that it is not the case.

Is the init_map()-call in print_fou_mapping() ok or shall I remove it
as well? I guess, at least in theory, showing fou sockets can lead to
resolving several unique interface indexes.

BR,
Kristian

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH iproute2-next v2] ip fou: Support binding FOU ports
  2019-04-22 11:29   ` Kristian Evensen
@ 2019-04-22 14:46     ` David Ahern
  0 siblings, 0 replies; 4+ messages in thread
From: David Ahern @ 2019-04-22 14:46 UTC (permalink / raw)
  To: Kristian Evensen; +Cc: Network Development

On 4/22/19 5:29 AM, Kristian Evensen wrote:
> Hi David,
> 
> On Sun, Apr 21, 2019 at 3:33 AM David Ahern <dsahern@gmail.com> wrote:
>> Missed this in v1: you definitely do not want to call ll_init_map here.
>> It does a full kink dump which can be expensive on large scale setups.
>> ll_name_to_index alone should be fine.
> 
> Thanks a lot for all your comments! It is not so strange that you
> missed the ll_init_map()-call in v1, as it was not there :) I have
> removed the call you commented and fou_parse_opt() works as intended.
> I initially thought that ll_init_map() was required for the different
> lookup-functions to work, but I see now that it is not the case.
> 
> Is the init_map()-call in print_fou_mapping() ok or shall I remove it
> as well? I guess, at least in theory, showing fou sockets can lead to
> resolving several unique interface indexes.
> 

I would only call ll_init_map for dumps that require a lot of link
information - e.g., link dump, address dump, interface stats, etc. For
use cases where the expectation is that only a few links are referenced,
those can be retrieved and added to the cache on an as used basis via
ll_index_to_name and ll_name_to_index.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-04-22 14:46 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-18 12:19 [PATCH iproute2-next v2] ip fou: Support binding FOU ports Kristian Evensen
2019-04-21  1:33 ` David Ahern
2019-04-22 11:29   ` Kristian Evensen
2019-04-22 14:46     ` David Ahern

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).