]> git.proxmox.com Git - mirror_iproute2.git/blame - ip/ipvrf.c
ip vrf: Fix reset to default VRF
[mirror_iproute2.git] / ip / ipvrf.c
CommitLineData
1949f82c
DA
1/*
2 * ipvrf.c "ip vrf"
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 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: David Ahern <dsa@cumulusnetworks.com>
10 *
11 */
12
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <sys/socket.h>
16#include <sys/mount.h>
17#include <linux/bpf.h>
18#include <linux/if.h>
19#include <fcntl.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <string.h>
24#include <errno.h>
25#include <limits.h>
26
27#include "rt_names.h"
28#include "utils.h"
29#include "ip_common.h"
30#include "bpf_util.h"
31
32#define CGRP_PROC_FILE "/cgroup.procs"
33
34static void usage(void)
35{
36 fprintf(stderr, "Usage: ip vrf exec [NAME] cmd ...\n");
37 fprintf(stderr, " ip vrf identify [PID]\n");
38 fprintf(stderr, " ip vrf pids [NAME]\n");
39
40 exit(-1);
41}
42
b5efa597 43static int vrf_identify(pid_t pid, char *name, size_t len)
1949f82c
DA
44{
45 char path[PATH_MAX];
46 char buf[4096];
47 char *vrf, *end;
b5efa597
DA
48 FILE *fp;
49
50 snprintf(path, sizeof(path), "/proc/%d/cgroup", pid);
51 fp = fopen(path, "r");
52 if (!fp)
53 return -1;
54
55 memset(name, 0, len);
56
57 while (fgets(buf, sizeof(buf), fp)) {
58 vrf = strstr(buf, "::/vrf/");
59 if (vrf) {
60 vrf += 7; /* skip past "::/vrf/" */
61 end = strchr(vrf, '\n');
62 if (end)
63 *end = '\0';
64
65 strncpy(name, vrf, len - 1);
66 break;
67 }
68 }
69
70 fclose(fp);
71
72 return 0;
73}
74
75static int ipvrf_identify(int argc, char **argv)
76{
77 char vrf[32];
78 int rc;
1949f82c 79 unsigned int pid;
1949f82c
DA
80
81 if (argc < 1)
82 pid = getpid();
83 else if (argc > 1)
84 invarg("Extra arguments specified\n", argv[1]);
85 else if (get_unsigned(&pid, argv[0], 10))
86 invarg("Invalid pid\n", argv[0]);
87
b5efa597
DA
88 rc = vrf_identify(pid, vrf, sizeof(vrf));
89 if (!rc) {
90 if (vrf[0] != '\0')
91 printf("%s\n", vrf);
92 } else {
93 fprintf(stderr, "Failed to lookup vrf association: %s\n",
94 strerror(errno));
1949f82c 95 }
1949f82c
DA
96
97 return rc;
98}
99
100static int ipvrf_pids(int argc, char **argv)
101{
102 char path[PATH_MAX];
103 char buf[4096];
104 char *mnt, *vrf;
105 int fd, rc = -1;
106 ssize_t n;
107
108 if (argc != 1) {
109 fprintf(stderr, "Invalid arguments\n");
110 return -1;
111 }
112
113 vrf = argv[0];
114
115 mnt = find_cgroup2_mount();
116 if (!mnt)
117 return -1;
118
119 snprintf(path, sizeof(path), "%s/vrf/%s%s", mnt, vrf, CGRP_PROC_FILE);
120 free(mnt);
121 fd = open(path, O_RDONLY);
122 if (fd < 0)
123 return 0; /* no cgroup file, nothing to show */
124
125 while (1) {
126 n = read(fd, buf, sizeof(buf) - 1);
127 if (n < 0) {
128 fprintf(stderr,
ab91aee4
SH
129 "Failed to read cgroups file: %s\n",
130 strerror(errno));
1949f82c
DA
131 break;
132 } else if (n == 0) {
133 rc = 0;
134 break;
135 }
136 printf("%s", buf);
137 }
138
139 close(fd);
140
141 return rc;
142}
143
144/* load BPF program to set sk_bound_dev_if for sockets */
145static char bpf_log_buf[256*1024];
146
147static int prog_load(int idx)
148{
149 struct bpf_insn prog[] = {
150 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
151 BPF_MOV64_IMM(BPF_REG_3, idx),
ab91aee4
SH
152 BPF_MOV64_IMM(BPF_REG_2,
153 offsetof(struct bpf_sock, bound_dev_if)),
154 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3,
155 offsetof(struct bpf_sock, bound_dev_if)),
1949f82c
DA
156 BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
157 BPF_EXIT_INSN(),
158 };
159
160 return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
161 "GPL", bpf_log_buf, sizeof(bpf_log_buf));
162}
163
164static int vrf_configure_cgroup(const char *path, int ifindex)
165{
166 int rc = -1, cg_fd, prog_fd = -1;
167
168 cg_fd = open(path, O_DIRECTORY | O_RDONLY);
169 if (cg_fd < 0) {
ab91aee4
SH
170 fprintf(stderr,
171 "Failed to open cgroup path: '%s'\n",
172 strerror(errno));
1949f82c
DA
173 goto out;
174 }
175
176 /*
177 * Load bpf program into kernel and attach to cgroup to affect
178 * socket creates
179 */
180 prog_fd = prog_load(ifindex);
181 if (prog_fd < 0) {
c94112fa
DA
182 fprintf(stderr, "Failed to load BPF prog: '%s'\n",
183 strerror(errno));
184 fprintf(stderr, "Kernel compiled with CGROUP_BPF enabled?\n");
1949f82c
DA
185 goto out;
186 }
187
188 if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
189 fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
190 strerror(errno));
1949f82c
DA
191 goto out;
192 }
193
194 rc = 0;
195out:
196 close(cg_fd);
197 close(prog_fd);
198
199 return rc;
200}
201
202static int vrf_switch(const char *name)
203{
204 char path[PATH_MAX], *mnt, pid[16];
2917b4f4 205 int ifindex = 0;
1949f82c
DA
206 int rc = -1, len, fd = -1;
207
2917b4f4
DA
208 if (strcmp(name, "default")) {
209 ifindex = name_is_vrf(name);
210 if (!ifindex) {
1949f82c
DA
211 fprintf(stderr, "Invalid VRF name\n");
212 return -1;
213 }
1949f82c
DA
214 }
215
216 mnt = find_cgroup2_mount();
217 if (!mnt)
218 return -1;
219
220 /* path to cgroup; make sure buffer has room to cat "/cgroup.procs"
221 * to the end of the path
222 */
2917b4f4
DA
223 len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE), "%s/vrf/%s",
224 mnt, ifindex ? name : "");
1949f82c
DA
225 if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) {
226 fprintf(stderr, "Invalid path to cgroup2 mount\n");
227 goto out;
228 }
229
230 if (make_path(path, 0755)) {
231 fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
232 goto out;
233 }
234
2917b4f4 235 if (ifindex && vrf_configure_cgroup(path, ifindex))
1949f82c
DA
236 goto out;
237
238 /*
239 * write pid to cgroup.procs making process part of cgroup
240 */
241 strcat(path, CGRP_PROC_FILE);
242 fd = open(path, O_RDWR | O_APPEND);
243 if (fd < 0) {
244 fprintf(stderr, "Failed to open cgroups.procs file: %s.\n",
245 strerror(errno));
246 goto out;
247 }
248
249 snprintf(pid, sizeof(pid), "%d", getpid());
250 if (write(fd, pid, strlen(pid)) < 0) {
251 fprintf(stderr, "Failed to join cgroup\n");
252 goto out;
253 }
254
255 rc = 0;
256out:
257 free(mnt);
258 close(fd);
259
260 return rc;
261}
262
263static int ipvrf_exec(int argc, char **argv)
264{
265 if (argc < 1) {
266 fprintf(stderr, "No VRF name specified\n");
267 return -1;
268 }
269 if (argc < 2) {
270 fprintf(stderr, "No command specified\n");
271 return -1;
272 }
273
274 if (vrf_switch(argv[0]))
275 return -1;
276
277 return -cmd_exec(argv[1], argv + 1, !!batch_mode);
278}
279
280int do_ipvrf(int argc, char **argv)
281{
282 if (argc == 0) {
283 fprintf(stderr, "No command given. Try \"ip vrf help\".\n");
284 exit(-1);
285 }
286
287 if (matches(*argv, "identify") == 0)
288 return ipvrf_identify(argc-1, argv+1);
289
290 if (matches(*argv, "pids") == 0)
291 return ipvrf_pids(argc-1, argv+1);
292
293 if (matches(*argv, "exec") == 0)
294 return ipvrf_exec(argc-1, argv+1);
295
296 if (matches(*argv, "help") == 0)
297 usage();
298
299 fprintf(stderr, "Command \"%s\" is unknown, try \"ip vrf help\".\n",
300 *argv);
301
302 exit(-1);
303}