improved load balancing algorithm.
[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 system(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
183 if (!s) return 0;
184 if (!s->opened) return 0;
185
186 sess = f->get_id_by_session(s);
187 if (flag == F_GARDEN)
188 {
189 f->log(2, sess, s->tunnel, "Garden user %s (%s)\n", s->user,
190 f->fmtaddr(htonl(s->ip), 0));
191
192 snprintf(cmd, sizeof(cmd),
193 "iptables -t nat -A garden_users -s %s -j garden",
194 f->fmtaddr(htonl(s->ip), 0));
195
196 f->log(3, sess, s->tunnel, "%s\n", cmd);
197 system(cmd);
198 s->walled_garden = 1;
199 }
200 else
201 {
202 sessionidt other;
203 int count = 40;
204
205 // Normal User
206 f->log(2, sess, s->tunnel, "Un-Garden user %s (%s)\n", s->user, f->fmtaddr(htonl(s->ip), 0));
207 if (newuser)
208 {
209 snprintf(s->user, MAXUSER, "%s", newuser);
210 f->log(2, sess, s->tunnel, " Setting username to %s\n", s->user);
211 }
212
213 // Kick off any duplicate usernames
214 // but make sure not to kick off ourself
215 if (s->ip && !s->die && (other = f->get_session_by_username(s->user)) &&
216 s != f->get_session_by_id(other))
217 {
218 f->sessionkill(other,
219 "Duplicate session when user released from walled garden");
220 }
221
222 /* Clean up counters */
223 s->pin = s->pout = 0;
224 s->cin = s->cout = 0;
225 s->cin_delta = s->cout_delta = 0;
226 s->cin_wrap = s->cout_wrap = 0;
227
228 snprintf(cmd, sizeof(cmd),
229 "iptables -t nat -D garden_users -s %s -j garden",
230 f->fmtaddr(htonl(s->ip), 0));
231
232 f->log(3, sess, s->tunnel, "%s\n", cmd);
233 while (--count)
234 {
235 int status = system(cmd);
236 if (WEXITSTATUS(status) != 0) break;
237 }
238
239 s->walled_garden = 0;
240
241 if (flag != F_CLEANUP)
242 {
243 /* OK, we're up! */
244 uint16_t r = f->radiusnew(f->get_id_by_session(s));
245 if (r) f->radiussend(r, RADIUSSTART);
246 }
247 }
248
249 return 1;
250 }
251
252 int plugin_init(struct pluginfuncs *funcs)
253 {
254 FILE *tables;
255 int found_nat = 0;
256
257 if (!funcs)
258 return 0;
259
260 f = funcs;
261
262 if ((tables = fopen("/proc/net/ip_tables_names", "r")))
263 {
264 char buf[1024];
265 while (fgets(buf, sizeof(buf), tables) && !found_nat)
266 found_nat = !strcmp(buf, "nat\n");
267
268 fclose(tables);
269 }
270
271 /* master killed/crashed? */
272 if (found_nat)
273 {
274 int i;
275 for (i = 0; down_commands[i] && *down_commands[i]; i++)
276 {
277 f->log(3, 0, 0, "Running %s\n", down_commands[i]);
278 system(down_commands[i]);
279 }
280 }
281
282 return 1;
283 }
284
285 void plugin_done()
286 {
287 int i;
288
289 if (!iam_master) // Never became master. nothing to do.
290 return;
291
292 for (i = 0; down_commands[i] && *down_commands[i]; i++)
293 {
294 f->log(3, 0, 0, "Running %s\n", down_commands[i]);
295 system(down_commands[i]);
296 }
297 }
298