diff options
Diffstat (limited to 'notes/projects/radius/sql/ippool-functions.sql')
| -rw-r--r-- | notes/projects/radius/sql/ippool-functions.sql | 536 |
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 | |||
| 10 | SET ROLE radius; | ||
| 11 | SET 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 | */ | ||
| 43 | CREATE 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 $$ | ||
| 52 | DECLARE | ||
| 53 | ipAddress INET; | ||
| 54 | resultAddress INET; | ||
| 55 | ipAdded INTEGER; | ||
| 56 | ipMask INTEGER; | ||
| 57 | BEGIN | ||
| 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; | ||
| 108 | END; | ||
| 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 | */ | ||
| 148 | CREATE 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 $$ | ||
| 161 | DECLARE | ||
| 162 | assignNasId INTEGER; | ||
| 163 | assignPortCount INTEGER; | ||
| 164 | BEGIN | ||
| 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; | ||
| 222 | END; | ||
| 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 | */ | ||
| 263 | CREATE 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 $$ | ||
| 276 | DECLARE | ||
| 277 | assignedIp INET; | ||
| 278 | assignNasId INTEGER; | ||
| 279 | BEGIN | ||
| 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; | ||
| 350 | END; | ||
| 351 | $$; | ||
| 352 | |||
| 353 | |||
| 354 | |||
| 355 | /* | ||
| 356 | * updates timestamp of addresses | ||
| 357 | * | ||
| 358 | * returns trigger | ||
| 359 | */ | ||
| 360 | CREATE OR REPLACE FUNCTION ip_release | ||
| 361 | ( | ||
| 362 | assignIp INET | ||
| 363 | ) | ||
| 364 | RETURNS BOOLEAN | ||
| 365 | LANGUAGE plpgsql | ||
| 366 | SECURITY DEFINER | ||
| 367 | AS $$ | ||
| 368 | DECLARE | ||
| 369 | BEGIN | ||
| 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; | ||
| 378 | END; | ||
| 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 | */ | ||
| 398 | CREATE 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 $$ | ||
| 407 | DECLARE | ||
| 408 | nasRegionId INTEGER; | ||
| 409 | returnId INTEGER; | ||
| 410 | BEGIN | ||
| 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; | ||
| 430 | END; | ||
| 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 | */ | ||
| 471 | CREATE 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 $$ | ||
| 484 | DECLARE | ||
| 485 | netRegionId INTEGER DEFAULT NULL; | ||
| 486 | netPoolId INTEGER; | ||
| 487 | returnId INTEGER DEFAULT 1; | ||
| 488 | BEGIN | ||
| 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; | ||
| 532 | END; | ||
| 533 | $$; | ||
| 534 | |||
| 535 | |||
| 536 | /* end of sql */ | ||
