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