1 /*
2  * seg6.c "ip sr/seg6"
3  *
4  *	  This program is free software; you can redistribute it and/or
5  *	  modify it under the terms of the GNU General Public License
6  *	  version 2 as published by the Free Software Foundation;
7  *
8  * Author: David Lebrun <david.lebrun@uclouvain.be>
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <arpa/inet.h>
19 #include <sys/ioctl.h>
20 #include <linux/if.h>
21 
22 #include <linux/genetlink.h>
23 #include <linux/seg6_genl.h>
24 #include <linux/seg6_hmac.h>
25 
26 #include "utils.h"
27 #include "ip_common.h"
28 #include "libgenl.h"
29 
30 #define HMAC_KEY_PROMPT "Enter secret for HMAC key ID (blank to delete): "
31 
usage(void)32 static void usage(void)
33 {
34 	fprintf(stderr, "Usage: ip sr { COMMAND | help }\n");
35 	fprintf(stderr, "	   ip sr hmac show\n");
36 	fprintf(stderr, "	   ip sr hmac set KEYID ALGO\n");
37 	fprintf(stderr, "	   ip sr tunsrc show\n");
38 	fprintf(stderr, "	   ip sr tunsrc set ADDRESS\n");
39 	fprintf(stderr, "where  ALGO := { sha1 | sha256 }\n");
40 	exit(-1);
41 }
42 
43 static struct rtnl_handle grth = { .fd = -1 };
44 static int genl_family = -1;
45 
46 #define SEG6_REQUEST(_req, _bufsiz, _cmd, _flags) \
47 	GENL_REQUEST(_req, _bufsiz, genl_family, 0, \
48 				SEG6_GENL_VERSION, _cmd, _flags)
49 
50 static struct {
51 	unsigned int cmd;
52 	struct in6_addr addr;
53 	__u32 keyid;
54 	const char *pass;
55 	__u8 alg_id;
56 } opts;
57 
process_msg(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)58 static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
59 		       void *arg)
60 {
61 	struct rtattr *attrs[SEG6_ATTR_MAX + 1];
62 	struct genlmsghdr *ghdr;
63 	FILE *fp = (FILE *)arg;
64 	int len = n->nlmsg_len;
65 
66 	if (n->nlmsg_type != genl_family)
67 		return -1;
68 
69 	len -= NLMSG_LENGTH(GENL_HDRLEN);
70 	if (len < 0)
71 		return -1;
72 
73 	ghdr = NLMSG_DATA(n);
74 
75 	parse_rtattr(attrs, SEG6_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
76 
77 	switch (ghdr->cmd) {
78 	case SEG6_CMD_DUMPHMAC:
79 	{
80 		char secret[64];
81 		char *algstr;
82 		__u8 slen = rta_getattr_u8(attrs[SEG6_ATTR_SECRETLEN]);
83 		__u8 alg_id = rta_getattr_u8(attrs[SEG6_ATTR_ALGID]);
84 
85 		memset(secret, 0, 64);
86 
87 		if (slen > 63) {
88 			fprintf(stderr, "HMAC secret length %d > 63, "
89 					"truncated\n", slen);
90 			slen = 63;
91 		}
92 		memcpy(secret, RTA_DATA(attrs[SEG6_ATTR_SECRET]), slen);
93 
94 		switch (alg_id) {
95 		case SEG6_HMAC_ALGO_SHA1:
96 			algstr = "sha1";
97 			break;
98 		case SEG6_HMAC_ALGO_SHA256:
99 			algstr = "sha256";
100 			break;
101 		default:
102 			algstr = "<unknown>";
103 		}
104 
105 		fprintf(fp, "hmac %u ",
106 			rta_getattr_u32(attrs[SEG6_ATTR_HMACKEYID]));
107 		fprintf(fp, "algo %s ", algstr);
108 		fprintf(fp, "secret \"%s\" ", secret);
109 
110 		fprintf(fp, "\n");
111 		break;
112 	}
113 	case SEG6_CMD_GET_TUNSRC:
114 	{
115 		fprintf(fp, "tunsrc addr %s\n",
116 			rt_addr_n2a(AF_INET6, 16,
117 				    RTA_DATA(attrs[SEG6_ATTR_DST])));
118 		break;
119 	}
120 	}
121 
122 	return 0;
123 }
124 
seg6_do_cmd(void)125 static int seg6_do_cmd(void)
126 {
127 	SEG6_REQUEST(req, 1024, opts.cmd, NLM_F_REQUEST);
128 	int repl = 0, dump = 0;
129 
130 	if (genl_family < 0) {
131 		if (rtnl_open_byproto(&grth, 0, NETLINK_GENERIC) < 0) {
132 			fprintf(stderr, "Cannot open generic netlink socket\n");
133 			exit(1);
134 		}
135 		genl_family = genl_resolve_family(&grth, SEG6_GENL_NAME);
136 		if (genl_family < 0)
137 			exit(1);
138 		req.n.nlmsg_type = genl_family;
139 	}
140 
141 	switch (opts.cmd) {
142 	case SEG6_CMD_SETHMAC:
143 	{
144 		addattr32(&req.n, sizeof(req), SEG6_ATTR_HMACKEYID, opts.keyid);
145 		addattr8(&req.n, sizeof(req), SEG6_ATTR_SECRETLEN,
146 			 strlen(opts.pass));
147 		addattr8(&req.n, sizeof(req), SEG6_ATTR_ALGID, opts.alg_id);
148 		if (strlen(opts.pass))
149 			addattr_l(&req.n, sizeof(req), SEG6_ATTR_SECRET,
150 				  opts.pass, strlen(opts.pass));
151 		break;
152 	}
153 	case SEG6_CMD_SET_TUNSRC:
154 		addattr_l(&req.n, sizeof(req), SEG6_ATTR_DST, &opts.addr,
155 			  sizeof(struct in6_addr));
156 		break;
157 	case SEG6_CMD_DUMPHMAC:
158 		dump = 1;
159 		break;
160 	case SEG6_CMD_GET_TUNSRC:
161 		repl = 1;
162 		break;
163 	}
164 
165 	if (!repl && !dump) {
166 		if (rtnl_talk(&grth, &req.n, NULL, 0) < 0)
167 			return -1;
168 	} else if (repl) {
169 		if (rtnl_talk(&grth, &req.n, &req.n, sizeof(req)) < 0)
170 			return -2;
171 		if (process_msg(NULL, &req.n, stdout) < 0) {
172 			fprintf(stderr, "Error parsing reply\n");
173 			exit(1);
174 		}
175 	} else {
176 		req.n.nlmsg_flags |= NLM_F_DUMP;
177 		req.n.nlmsg_seq = grth.dump = ++grth.seq;
178 		if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
179 			perror("Failed to send dump request");
180 			exit(1);
181 		}
182 
183 		if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
184 			fprintf(stderr, "Dump terminated\n");
185 			exit(1);
186 		}
187 	}
188 
189 	return 0;
190 }
191 
do_seg6(int argc,char ** argv)192 int do_seg6(int argc, char **argv)
193 {
194 	if (argc < 1 || matches(*argv, "help") == 0)
195 		usage();
196 
197 	memset(&opts, 0, sizeof(opts));
198 
199 	if (matches(*argv, "hmac") == 0) {
200 		NEXT_ARG();
201 		if (matches(*argv, "show") == 0) {
202 			opts.cmd = SEG6_CMD_DUMPHMAC;
203 		} else if (matches(*argv, "set") == 0) {
204 			NEXT_ARG();
205 			if (get_u32(&opts.keyid, *argv, 0) || opts.keyid == 0)
206 				invarg("hmac KEYID value is invalid", *argv);
207 			NEXT_ARG();
208 			if (strcmp(*argv, "sha1") == 0) {
209 				opts.alg_id = SEG6_HMAC_ALGO_SHA1;
210 			} else if (strcmp(*argv, "sha256") == 0) {
211 				opts.alg_id = SEG6_HMAC_ALGO_SHA256;
212 			} else {
213 				invarg("hmac ALGO value is invalid", *argv);
214 			}
215 			opts.cmd = SEG6_CMD_SETHMAC;
216 #ifndef __BIONIC__
217 			opts.pass = getpass(HMAC_KEY_PROMPT);
218 #endif
219 		} else {
220 			invarg("unknown", *argv);
221 		}
222 	} else if (matches(*argv, "tunsrc") == 0) {
223 		NEXT_ARG();
224 		if (matches(*argv, "show") == 0) {
225 			opts.cmd = SEG6_CMD_GET_TUNSRC;
226 		} else if (matches(*argv, "set") == 0) {
227 			NEXT_ARG();
228 			opts.cmd = SEG6_CMD_SET_TUNSRC;
229 			if (!inet_get_addr(*argv, NULL, &opts.addr))
230 				invarg("tunsrc ADDRESS value is invalid",
231 				       *argv);
232 		} else {
233 			invarg("unknown", *argv);
234 		}
235 	} else {
236 		invarg("unknown", *argv);
237 	}
238 
239 	return seg6_do_cmd();
240 }
241