aboutsummaryrefslogtreecommitdiffstats
path: root/notes/projects/radius/sql/ippool-functions.sql
diff options
context:
space:
mode:
Diffstat (limited to 'notes/projects/radius/sql/ippool-functions.sql')
-rw-r--r--notes/projects/radius/sql/ippool-functions.sql536
1 files changed, 536 insertions, 0 deletions
diff --git a/notes/projects/radius/sql/ippool-functions.sql b/notes/projects/radius/sql/ippool-functions.sql
new file mode 100644
index 0000000..bbfd60f
--- /dev/null
+++ b/notes/projects/radius/sql/ippool-functions.sql
@@ -0,0 +1,536 @@
1/*
2 * IP Engineering RADIUS Servers
3 *
4 * ippool-schema.sql - builds database schema for RADIUS IP Pools
5 *
6 * Usage: psql -f ippool-schema.sql radius_ippool
7 */
8
9
10SET ROLE radius;
11SET client_min_messages TO WARNING;
12\set ON_ERROR_STOP on
13
14
15-- /////////////////
16-- // //
17-- // Functions //
18-- // //
19-- /////////////////
20
21/*
22 * Creates IP entries for the given network block
23 *
24 * % SELECT ip_add_network(
25 * ipNetwork := '198.51.100.0/24'
26 * );
27 * % SELECT ip_add_network(
28 * ipNetblockId := (
29 * SELECT id
30 * FROM netblock
31 * WHERE network = '198.51.100.0/24'
32 * )
33 * );
34 *
35 * ipNetwork
36 * : network address and network size being added to database
37 *
38 * ipNetblockId
39 * : reference to netblock to add to database
40 *
41 * Returns the number of new IP addresses registered
42 */
43CREATE OR REPLACE FUNCTION ip_add_network
44(
45 ipNetwork CIDR DEFAULT NULL,
46 ipNetblockId INTEGER DEFAULT NULL
47)
48 RETURNS INTEGER
49 LANGUAGE plpgsql
50 SECURITY DEFINER
51 AS $$
52DECLARE
53 ipAddress INET;
54 resultAddress INET;
55 ipAdded INTEGER;
56 ipMask INTEGER;
57BEGIN
58
59 -- verifies input
60 IF ( ( ipNetwork IS NULL ) AND ( ipNetblockId IS NULL ) ) THEN
61 RAISE EXCEPTION 'must specify either ipNetwork or ipNetblockId';
62 END IF;
63
64 -- determines netblock
65 if ( ipNetblockId IS NULL ) THEN
66 SELECT id INTO ipNetblockId
67 FROM netblock
68 WHERE network >>= ipNetwork;
69 ELSE
70 SELECT network INTO ipNetwork
71 FROM netblock
72 WHERE id = ipNetblockId;
73 END IF;
74 if ( ( ipNetblockId IS NULL ) OR ( ipNetwork IS NULL ) ) THEN
75 RAISE EXCEPTION 'specified netblock does not exist';
76 END IF;
77
78 -- restricts access to IPv4 addresses
79 IF ( family(ipNetwork) != 4 ) THEN
80 RAISE EXCEPTION 'only IPv4 addresses are supported';
81 END IF;
82
83 -- sets starting values
84 ipAdded := 0;
85 ipAddress := set_masklen( network(ipNetwork), 32 );
86
87 -- loops through individual IP addresses
88 LOOP
89 EXIT WHEN ipAddress > set_masklen( broadcast(ipNetwork), 32 );
90
91 INSERT INTO ip
92 ( address,
93 netblockId
94 )
95 SELECT ipAddress,
96 ipNetblockId
97 WHERE NOT EXISTS
98 ( SELECT address FROM ip WHERE address = ipAddress )
99 RETURNING address INTO resultAddress;
100 IF ( resultAddress IS NOT NULL ) THEN
101 ipAdded := ipAdded + 1;
102 END IF;
103 ipAddress := ipAddress + 1;
104
105 END LOOP;
106
107 RETURN ipAdded;
108END;
109$$;
110
111
112/*
113 * Assigns specific IP address to specified user/session
114 *
115 * % SELECT ip_assign(
116 * assignIp := '198.51.100.1/32',
117 * assignUser := 'jdoe@acsalaska.net'
118 * );
119 * % SELECT ip_assign(
120 * assignIp := '198.51.100.1/32',
121 * assignUser := 'jdoe@acsalaska.net',
122 * assignPortLimit := 5,
123 * assignNas := 'br1.cwc',
124 * assignSess := '596c3b82-75df-11e9-bcbb-6003089b9f24',
125 * assignForce := FALSE
126 * );
127 *
128 * assignIp
129 * : IP address to assign to user
130 *
131 * assignUser
132 * : user to assign IP address
133 *
134 * assignPortLimit
135 * : specifies maximum IP assignments per user allowed
136 *
137 * assignNas
138 * : session NAS (attempt to guess if not specified)
139 *
140 * assignSess
141 * : RADIUS session
142 *
143 * assignForce
144 * : force IP assignment, even if netblock is inactive (i.e. static assignments)
145 *
146 * Returns TRUE if assigned and FALSE if not assigned
147 */
148CREATE OR REPLACE FUNCTION ip_assign
149(
150 assignIp INET,
151 assignUser TEXT,
152 assignPortLimit INTEGER DEFAULT 2,
153 assignNas TEXT DEFAULT NULL,
154 assignSess TEXT DEFAULT NULL,
155 assignForce BOOLEAN DEFAULT FALSE
156)
157 RETURNS BOOLEAN
158 LANGUAGE plpgsql
159 SECURITY DEFINER
160 AS $$
161DECLARE
162 assignNasId INTEGER;
163 assignPortCount INTEGER;
164BEGIN
165
166 -- sets defaults
167 IF ( assignSess IS NULL ) THEN
168 assignSess := 'user:' || assignUser;
169 END IF;
170
171 -- tallies existing sessions
172 SELECT count(id)
173 INTO assignPortCount
174 FROM ip
175 WHERE ip.username LIKE assignUser
176 AND ip.nasId IS NOT NULL
177 AND ip.address != assignIp;
178 IF ( assignPortCount >= assignPortLimit ) THEN
179 RETURN FALSE;
180 END IF;
181
182 -- verifies IP address is not inactive
183 IF ( assignForce = FALSE ) THEN
184 SELECT network
185 FROM netblock
186 WHERE netblock.network >>= assignIp
187 AND netblock.active IS FALSE;
188 IF FOUND THEN
189 RETURN FALSE;
190 END IF;
191 END IF;
192
193 -- attempts to determine NAS
194 IF ( assignNas IS NOT NULL ) THEN
195 SELECT id
196 INTO assignNasId
197 FROM nas
198 WHERE name LIKE assignNas;
199 ELSE
200 SELECT nas.id
201 INTO assignNasId
202 FROM nas
203 INNER JOIN region
204 ON region.id = nas.regionId
205 INNER JOIN netblock
206 ON netblock.regionId = regoin.id
207 WHERE netblock.network >>= assignIp;
208 END IF;
209 IF ( assignNasId IS NULL ) THEN
210 RETURN FALSE;
211 END IF;
212
213 -- updates record
214 UPDATE ip
215 SET nasId = ( SELECT id FROM nas WHERE name LIKE assignNas ),
216 username = assignUser,
217 acctSession = assignSess
218 WHERE address = set_masklen( assignIp, 32 )
219 AND nasId IS NULL;
220
221 RETURN FOUND;
222END;
223$$;
224
225
226/*
227 * Auto-assigns IP address to specified user/session
228 *
229 * % SELECT ip_autoassign(
230 * assignUser := 'jdoe@acsalaska.net',
231 * assignNas := 'br1.sol',
232 * assignPool := 'dhcp'
233 * );
234 * % SELECT ip_autoassign(
235 * assignUser := 'jdoe@acsalaska.net',
236 * assignNas := 'br1.sol',
237 * assignPool := 'dhcp',
238 * assignPortLimit := 5,
239 * assignSess := 'b4356b24-7670-11e9-84a0-6003089b9f24',
240 * assignPrevIp := TRUE
241 * );
242 *
243 * assignUser
244 * : user to assign IP address
245 *
246 * assignNas
247 * : NAS to assign to IP address and implies region from which to pull IP
248 *
249 * assignPool
250 * : IP pool to assign from which to assign IP address
251 *
252 * assignPortLimit
253 * : specifies maximum IP assignments per user allowed
254 *
255 * assignSess (default: auto-generated from username)
256 * : RADIUS session
257 *
258 * assignPrevIp (default: TRUE)
259 * : attempt to re-assign user's previous IP address
260 *
261 * Returns INET with value of assigned IP address
262 */
263CREATE OR REPLACE FUNCTION ip_autoassign
264(
265 assignUser TEXT,
266 assignNas TEXT,
267 assignPool TEXT,
268 assignPortLimit INTEGER, DEFAULT 2,
269 assignSess TEXT DEFAULT NULL,
270 assignPrevIp BOOLEAN DEFAULT TRUE
271)
272 RETURNS INET
273 LANGUAGE plpgsql
274 SECURITY DEFINER
275 AS $$
276DECLARE
277 assignedIp INET;
278 assignNasId INTEGER;
279BEGIN
280
281 -- sets defaults
282 IF ( assignSess IS NULL ) THEN
283 assignSess := 'user:' || assignUser;
284 END IF;
285
286 -- search for user's previous address
287 IF ( assignPrevIp IS TRUE ) THEN
288 SELECT ip.address,
289 nas.id
290 INTO assignedIp,
291 assignNasId
292 FROM ip
293 INNER JOIN netblock
294 ON netblock.network >>= ip.address
295 INNER JOIN region
296 ON region.id = netblock.regionId
297 INNER JOIN pool
298 ON pool.id = netblock.poolId
299 INNER JOIN nas
300 ON nas.regionId = region.id
301 WHERE ip.nasId IS NULL
302 AND ip.username LIKE lower( assignUser )
303 AND nas.name LIKE lower( assignNas )
304 AND pool.name LIKE lower( assignPool )
305 AND netblock.active = TRUE
306 ORDER BY ip.modified DESC,
307 ip.address ASC
308 LIMIT 1;
309 END IF;
310
311 -- if previous address is unavailable, find longest idle address
312 IF ( assignedIp IS NULL ) THEN
313 SELECT ip.address,
314 nas.id
315 INTO assignedIp,
316 assignNasId
317 FROM ip
318 INNER JOIN netblock
319 ON netblock.network >>= ip.address
320 INNER JOIN region
321 ON region.id = netblock.regionId
322 INNER JOIN pool
323 ON pool.id = netblock.poolId
324 INNER JOIN nas
325 ON nas.regionId = region.id
326 WHERE ip.nasId IS NULL
327 AND ip.username LIKE lower( assignUser )
328 AND nas.name LIKE lower( assignNas )
329 AND pool.name LIKE lower( assignPool )
330 AND netblock.active = TRUE
331 ORDER BY ip.modified DESC,
332 ip.address ASC
333 LIMIT 1;
334 IF NOT FOUND THEN
335 RETURN NULL;
336 END IF;
337 END IF;
338
339 -- update record
340 UPDATE ip
341 SET nasId = assignNasId,
342 username = assignUser,
343 acctSession = assignSess
344 WHERE address = assignedIp;
345 IF NOT FOUND THEN
346 RETURN NULL;
347 END IF;
348
349 RETURN assignedIp;
350END;
351$$;
352
353
354
355/*
356 * updates timestamp of addresses
357 *
358 * returns trigger
359 */
360CREATE OR REPLACE FUNCTION ip_release
361(
362 assignIp INET
363)
364 RETURNS BOOLEAN
365 LANGUAGE plpgsql
366 SECURITY DEFINER
367 AS $$
368DECLARE
369BEGIN
370
371 -- updates record
372 UPDATE ip
373 SET nasId = NULL,
374 acctSession = NULL
375 WHERE address = set_masklen( assignIp, 32 );
376
377 RETURN FOUND;
378END;
379$$;
380
381
382/*
383 * Creates entry for NAS client
384 *
385 * % SELECT nas_add(
386 * nasName := 'br1.cwc',
387 * regionName := 'Anchorage'
388 * );
389 *
390 * nasName
391 * : name of NAS being added to database
392 *
393 * regionName
394 * : the name of the region in which the NAS is physically located
395 *
396 * Returns the `id` of the created nas client
397 */
398CREATE OR REPLACE FUNCTION nas_add
399(
400 nasName VARCHAR(32),
401 regionName VARCHAR(128)
402)
403 RETURNS INTEGER
404 LANGUAGE plpgsql
405 SECURITY DEFINER
406 AS $$
407DECLARE
408 nasRegionId INTEGER;
409 returnId INTEGER;
410BEGIN
411 -- searches for region id
412 SELECT id INTO nasRegionId
413 FROM region
414 WHERE name LIKE lower(regionName);
415 IF ( nasRegionId IS NULL ) THEN
416 RAISE EXCEPTION 'invalid region';
417 END IF;
418
419 -- adds new pool
420 INSERT INTO nas
421 ( name,
422 regionId
423 )
424 VALUES ( lower(nasName),
425 nasRegionId
426 )
427 RETURNING id INTO returnId;
428
429 RETURN returnId;
430END;
431$$;
432
433
434/*
435 * Creates entry for network block
436 *
437 * % SELECT netblock_add(
438 * netAddress := '198.51.100.0/24',
439 * poolName := 'PPPoE'
440 * );
441 * % SELECT netblock_add(
442 * netAddress := '203.0.113.0/24',
443 * poolName := 'DHCP',
444 * regionName := 'Soldotna',
445 * netActive := FALSE,
446 * netDesc := 'Lab IP Pool',
447 * addIpEntries := TRUE
448 * );
449 *
450 * netAddress
451 * : network address and network size being added to database
452 *
453 * poolName
454 * : IP pool network is being assigned
455 *
456 * regionName
457 * : the name of the region in which the network will physically serve
458 *
459 * netActive
460 * : TRUE if addresses within network are assignable, FALSE if addresses are
461 * : not assignable
462 *
463 * netDesc
464 * : human readable description of the network
465 *
466 * addIpEntries
467 * : add coorisponding ip table entries for network
468 *
469 * Returns the `id` of the created nas client
470 */
471CREATE OR REPLACE FUNCTION netblock_add
472(
473 netAddress CIDR,
474 poolName VARCHAR(32),
475 regionName VARCHAR(32) DEFAULT NULL,
476 netActive BOOLEAN DEFAULT TRUE,
477 netDesc VARCHAR(128) DEFAULT NULL,
478 addIpEntries BOOLEAN DEFAULT TRUE
479)
480 RETURNS INTEGER
481 LANGUAGE plpgsql
482 SECURITY DEFINER
483 AS $$
484DECLARE
485 netRegionId INTEGER DEFAULT NULL;
486 netPoolId INTEGER;
487 returnId INTEGER DEFAULT 1;
488BEGIN
489
490 -- restricts access to IPv4 addresses
491 IF ( family(netAddress) != 4 ) THEN
492 RAISE EXCEPTION 'only IPv4 addresses are supported';
493 END IF;
494
495 -- searches for region id
496 if ( regionName IS NOT NULL ) THEN
497 SELECT id INTO netRegionId
498 FROM region
499 WHERE name LIKE lower(regionName);
500 END IF;
501
502 -- searches for pool id
503 SELECT id INTO netPoolId
504 FROM pool
505 WHERE name LIKE lower(poolName);
506 IF ( netPoolId IS NULL ) THEN
507 RAISE EXCEPTION 'invalid pool';
508 END IF;
509
510 -- adds new pool
511 INSERT INTO netblock
512 ( network,
513 poolId,
514 regionId,
515 active,
516 description
517 )
518 VALUES ( netAddress,
519 netPoolId,
520 netRegionId,
521 netActive,
522 netDesc
523 )
524 RETURNING id INTO returnId;
525
526 -- adds IP entries
527 if ( addIpEntries IS NOT FALSE ) THEN
528 PERFORM ip_add_network( ipNetblockId := returnId);
529 END IF;
530
531 RETURN returnId;
532END;
533$$;
534
535
536/* end of sql */