Initial Release
[project/omcproxy.git] / src / omcproxy.c
1 /*
2 * Copyright 2015 Steven Barth <steven at midlink.org>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #include <stdio.h>
19 #include <signal.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <netdb.h>
23 #include <net/if.h>
24 #include <unistd.h>
25
26 #include <libubox/uloop.h>
27 #include <libubox/blobmsg.h>
28
29 #include "omcproxy.h"
30 #include "proxy.h"
31
32 int log_level = LOG_WARNING;
33
34
35 enum {
36 PROXY_ATTR_SOURCE,
37 PROXY_ATTR_SCOPE,
38 PROXY_ATTR_DEST,
39 PROXY_ATTR_MAX,
40 };
41
42 static struct blobmsg_policy proxy_policy[PROXY_ATTR_MAX] = {
43 [PROXY_ATTR_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_STRING },
44 [PROXY_ATTR_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
45 [PROXY_ATTR_DEST] = { .name = "dest", .type = BLOBMSG_TYPE_ARRAY },
46 };
47
48 static int handle_proxy_set(void *data, size_t len)
49 {
50 struct blob_attr *tb[PROXY_ATTR_MAX], *c;
51 blobmsg_parse(proxy_policy, PROXY_ATTR_MAX, tb, data, len);
52
53 const char *name = ((c = tb[PROXY_ATTR_SOURCE])) ? blobmsg_get_string(c) : NULL;
54 int uplink = 0;
55 int downlinks[32] = {0};
56 size_t downlinks_cnt = 0;
57 enum proxy_flags flags = 0;
58
59 if (!name)
60 return -EINVAL;
61
62 if (!(uplink = if_nametoindex(name))) {
63 L_WARN("%s(%s): %s", __FUNCTION__, name, strerror(errno));
64 return -errno;
65 }
66
67 if ((c = tb[PROXY_ATTR_SCOPE])) {
68 const char *scope = blobmsg_get_string(c);
69 if (!strcmp(scope, "global"))
70 flags = PROXY_GLOBAL;
71 else if (!strcmp(scope, "organization"))
72 flags = PROXY_ORGLOCAL;
73 else if (!strcmp(scope, "site"))
74 flags = PROXY_SITELOCAL;
75 else if (!strcmp(scope, "admin"))
76 flags = PROXY_ADMINLOCAL;
77 else if (!strcmp(scope, "realm"))
78 flags = PROXY_REALMLOCAL;
79
80 if (!flags) {
81 L_WARN("%s(%s): invalid scope (%s)", __FUNCTION__, name, scope);
82 return -EINVAL;
83 }
84 }
85
86 if ((c = tb[PROXY_ATTR_DEST])) {
87 struct blob_attr *d;
88 unsigned rem;
89 blobmsg_for_each_attr(d, c, rem) {
90 if (downlinks_cnt >= 32) {
91 L_WARN("%s(%s): maximum number of destinations exceeded", __FUNCTION__, name);
92 return -EINVAL;
93 }
94
95 const char *n = blobmsg_type(d) == BLOBMSG_TYPE_STRING ? blobmsg_get_string(d) : "";
96 if (!(downlinks[downlinks_cnt++] = if_nametoindex(n))) {
97 L_WARN("%s(%s): %s (%s)", __FUNCTION__, name, strerror(errno), blobmsg_get_string(d));
98 return -errno;
99 }
100 }
101 }
102
103 return proxy_set(uplink, downlinks, downlinks_cnt, flags);
104 }
105
106 static void handle_signal(__unused int signal)
107 {
108 uloop_end();
109 }
110
111 static void usage(const char *arg) {
112 fprintf(stderr, "Usage: %s [options] <proxy1> [<proxy2>] [...]\n"
113 "\nProxy examples:\n"
114 "eth1,eth2\n"
115 "eth1,eth2,eth3,scope=organization\n"
116 "\nProxy options (each option may only occur once):\n"
117 " <interface> interfaces to proxy (first is uplink)\n"
118 " scope=<scope> minimum multicast scope to proxy\n"
119 " [global,organization,site,admin,realm] (default: global)\n"
120 "\nOptions:\n"
121 " -v verbose logging\n"
122 " -h show this help\n",
123 arg);
124 }
125
126 int main(int argc, char **argv) {
127 signal(SIGINT, handle_signal);
128 signal(SIGTERM, handle_signal);
129 signal(SIGHUP, SIG_IGN);
130 signal(SIGPIPE, SIG_IGN);
131 openlog("omcproxy", LOG_PERROR, LOG_DAEMON);
132
133 if (getuid()) {
134 L_ERR("must be run as root!");
135 return 2;
136 }
137
138 uloop_init();
139 bool start = true;
140
141 for (ssize_t i = 1; i < argc; ++i) {
142 const char *source = NULL;
143 const char *scope = NULL;
144 struct blob_buf b = {NULL, NULL, 0, NULL};
145
146 if (!strcmp(argv[i], "-h")) {
147 usage(argv[0]);
148 return 1;
149 } else if (!strncmp(argv[i], "-v", 2)) {
150 if ((log_level = atoi(&argv[i][2])) <= 0)
151 log_level = 7;
152 continue;
153 }
154
155
156 blob_buf_init(&b, 0);
157
158 void *k = blobmsg_open_array(&b, "dest");
159 for (char *c = strtok(argv[i], ","); c; c = strtok(NULL, ",")) {
160 if (!strncmp(c, "scope=", 6)) {
161 scope = &c[6];
162 } else if (!source) {
163 source = c;
164 } else {
165 blobmsg_add_string(&b, NULL, c);
166 }
167 }
168 blobmsg_close_array(&b, k);
169
170 if (source)
171 blobmsg_add_string(&b, "source", source);
172
173 if (scope)
174 blobmsg_add_string(&b, "scope", scope);
175
176 if (handle_proxy_set(blob_data(b.head), blob_len(b.head))) {
177 fprintf(stderr, "failed to setup proxy: %s\n", argv[i]);
178 start = false;
179 }
180
181 blob_buf_free(&b);
182 }
183
184 if (argc < 2) {
185 usage(argv[0]);
186 start = false;
187 }
188
189 if (start)
190 uloop_run();
191
192 proxy_update(true);
193 proxy_flush();
194
195 uloop_done();
196 return 0;
197 }