Merge from master
[l2tpns.git] / garden.c
1 #include <string.h>
2 #include <malloc.h>
3 #include <stdlib.h>
4 #include <sys/wait.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <linux/rtnetlink.h>
8
9 #include "l2tpns.h"
10 #include "plugin.h"
11 #include "control.h"
12
13 /* walled garden */
14
15 int plugin_api_version = PLUGIN_API_VERSION;
16 static struct pluginfuncs *f = 0;
17
18 static int iam_master = 0; // We're all slaves! Slaves I tell you!
19
20 char *up_commands[] = {
21 "iptables -t nat -N garden >/dev/null 2>&1", // Create a chain that all gardened users will go through
22 "iptables -t nat -F garden",
23 ". " PLUGINCONF "/build-garden", // Populate with site-specific DNAT rules
24 "iptables -t nat -N garden_users >/dev/null 2>&1", // Empty chain, users added/removed by garden_session
25 "iptables -t nat -F garden_users",
26 "iptables -t nat -A PREROUTING -j garden_users", // DNAT any users on the garden_users chain
27 "sysctl -w net.ipv4.netfilter.ip_conntrack_max=512000" // lots of entries
28 " net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=18000 >/dev/null", // 5hrs
29 NULL,
30 };
31
32 char *down_commands[] = {
33 "iptables -t nat -F PREROUTING",
34 "iptables -t nat -F garden_users",
35 "iptables -t nat -X garden_users",
36 "iptables -t nat -F garden",
37 "iptables -t nat -X garden",
38 "rmmod iptable_nat", // Should also remove ip_conntrack, but
39 // doing so can take hours... literally.
40 // If a master is re-started as a slave,
41 // either rmmod manually, or reboot.
42 NULL,
43 };
44
45 #define F_UNGARDEN 0
46 #define F_GARDEN 1
47 #define F_CLEANUP 2
48
49 int garden_session(sessiont *s, int flag, char *newuser);
50
51 int plugin_post_auth(struct param_post_auth *data)
52 {
53 // Ignore if user authentication was successful
54 if (data->auth_allowed)
55 return PLUGIN_RET_OK;
56
57 f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
58 "Walled Garden allowing login\n");
59
60 data->auth_allowed = 1;
61 data->s->walled_garden = 1;
62 return PLUGIN_RET_OK;
63 }
64
65 int plugin_new_session(struct param_new_session *data)
66 {
67 if (!iam_master)
68 return PLUGIN_RET_OK; // Slaves don't do walled garden processing.
69
70 if (data->s->walled_garden)
71 garden_session(data->s, F_GARDEN, 0);
72
73 return PLUGIN_RET_OK;
74 }
75
76 int plugin_kill_session(struct param_new_session *data)
77 {
78 if (!iam_master)
79 return PLUGIN_RET_OK; // Slaves don't do walled garden processing.
80
81 if (data->s->walled_garden)
82 garden_session(data->s, F_CLEANUP, 0);
83
84 return PLUGIN_RET_OK;
85 }
86
87 char *plugin_control_help[] = {
88 " garden USER|SID Put user into the walled garden",
89 " ungarden SID [USER] Release session from garden",
90 0
91 };
92
93 int plugin_control(struct param_control *data)
94 {
95 sessionidt session;
96 sessiont *s = 0;
97 int flag;
98 char *end;
99
100 if (data->argc < 1)
101 return PLUGIN_RET_OK;
102
103 if (strcmp(data->argv[0], "garden") && strcmp(data->argv[0], "ungarden"))
104 return PLUGIN_RET_OK; // not for us
105
106 if (!iam_master)
107 return PLUGIN_RET_NOTMASTER;
108
109 flag = data->argv[0][0] == 'g' ? F_GARDEN : F_UNGARDEN;
110
111 if (data->argc < 2 || data->argc > 3 || (data->argc > 2 && flag == F_GARDEN))
112 {
113 data->response = NSCTL_RES_ERR;
114 data->additional = flag == F_GARDEN
115 ? "requires username or session id"
116 : "requires session id and optional username";
117
118 return PLUGIN_RET_STOP;
119 }
120
121 if (!(session = strtol(data->argv[1], &end, 10)) || *end)
122 {
123 if (flag)
124 session = f->get_session_by_username(data->argv[1]);
125 else
126 session = 0; // can't ungarden by username
127 }
128
129 if (session)
130 s = f->get_session_by_id(session);
131
132 if (!s || !s->ip)
133 {
134 data->response = NSCTL_RES_ERR;
135 data->additional = "session not found";
136 return PLUGIN_RET_STOP;
137 }
138
139 if (s->walled_garden == flag)
140 {
141 data->response = NSCTL_RES_ERR;
142 data->additional = flag ? "already in walled garden" : "not in walled garden";
143 return PLUGIN_RET_STOP;
144 }
145
146 garden_session(s, flag, data->argc > 2 ? data->argv[2] : 0);
147 f->session_changed(session);
148
149 data->response = NSCTL_RES_OK;
150 data->additional = 0;
151
152 return PLUGIN_RET_STOP;
153 }
154
155 int plugin_become_master(void)
156 {
157 int i;
158 iam_master = 1; // We just became the master. Wow!
159
160 for (i = 0; up_commands[i] && *up_commands[i]; i++)
161 {
162 f->log(3, 0, 0, "Running %s\n", up_commands[i]);
163 if (-1 == system(up_commands[i])) f->log(0, 0, 0, "error command %s\n", up_commands[i]);
164 }
165
166 return PLUGIN_RET_OK;
167 }
168
169 // Called for each active session after becoming master
170 int plugin_new_session_master(sessiont *s)
171 {
172 if (s->walled_garden)
173 garden_session(s, F_GARDEN, 0);
174
175 return PLUGIN_RET_OK;
176 }
177
178 int garden_session(sessiont *s, int flag, char *newuser)
179 {
180 char cmd[2048];
181 sessionidt sess;
182 int status;
183
184 if (!s) return 0;
185 if (!s->opened) return 0;
186
187 sess = f->get_id_by_session(s);
188 if (flag == F_GARDEN)
189 {
190 f->log(2, sess, s->tunnel, "Garden user %s (%s)\n", s->user,
191 f->fmtaddr(htonl(s->ip), 0));
192
193 snprintf(cmd, sizeof(cmd),
194 "iptables -t nat -A garden_users -s %s -j garden",
195 f->fmtaddr(htonl(s->ip), 0));
196
197 f->log(3, sess, s->tunnel, "%s\n", cmd);
198 status = system(cmd);
199 s->walled_garden = 1;
200 }
201 else
202 {
203 sessionidt other;
204 int count = 40;
205
206 // Normal User
207 f->log(2, sess, s->tunnel, "Un-Garden user %s (%s)\n", s->user, f->fmtaddr(htonl(s->ip), 0));
208 if (newuser)
209 {
210 snprintf(s->user, MAXUSER, "%s", newuser);
211 f->log(2, sess, s->tunnel, " Setting username to %s\n", s->user);
212 }
213
214 // Kick off any duplicate usernames
215 // but make sure not to kick off ourself
216 if (s->ip && !s->die && (other = f->get_session_by_username(s->user)) &&
217 s != f->get_session_by_id(other))
218 {
219 f->sessionkill(other,
220 "Duplicate session when user released from walled garden");
221 }
222
223 /* Clean up counters */
224 s->pin = s->pout = 0;
225 s->cin = s->cout = 0;
226 s->cin_delta = s->cout_delta = 0;
227 s->cin_wrap = s->cout_wrap = 0;
228
229 snprintf(cmd, sizeof(cmd),
230 "iptables -t nat -D garden_users -s %s -j garden",
231 f->fmtaddr(htonl(s->ip), 0));
232
233 f->log(3, sess, s->tunnel, "%s\n", cmd);
234 while (--count)
235 {
236 status = system(cmd);
237 if (WEXITSTATUS(status) != 0) break;
238 }
239
240 s->walled_garden = 0;
241
242 if (flag != F_CLEANUP)
243 {
244 /* OK, we're up! */
245 uint16_t r = f->radiusnew(f->get_id_by_session(s));
246 if (r) f->radiussend(r, RADIUSSTART);
247 }
248 }
249
250 return 1;
251 }
252
253 int plugin_init(struct pluginfuncs *funcs)
254 {
255 FILE *tables;
256 int found_nat = 0;
257
258 if (!funcs)
259 return 0;
260
261 f = funcs;
262
263 if ((tables = fopen("/proc/net/ip_tables_names", "r")))
264 {
265 char buf[1024];
266 while (fgets(buf, sizeof(buf), tables) && !found_nat)
267 found_nat = !strcmp(buf, "nat\n");
268
269 fclose(tables);
270 }
271
272 /* master killed/crashed? */
273 if (found_nat)
274 {
275 int i;
276 for (i = 0; down_commands[i] && *down_commands[i]; i++)
277 {
278 f->log(3, 0, 0, "Running %s\n", down_commands[i]);
279 if (-1 == system(down_commands[i])) f->log(0, 0, 0, "error command %s\n", down_commands[i]);
280 }
281 }
282
283 return 1;
284 }
285
286 void plugin_done()
287 {
288 int i;
289
290 if (!iam_master) // Never became master. nothing to do.
291 return;
292
293 for (i = 0; down_commands[i] && *down_commands[i]; i++)
294 {
295 f->log(3, 0, 0, "Running %s\n", down_commands[i]);
296 if (-1 == system(down_commands[i])) f->log(0, 0, 0, "error command %s\n", down_commands[i]);
297 }
298 }
299