aboutsummaryrefslogtreecommitdiffstats
path: root/tools/dhcpns/config/dhcp4.py
blob: eeabe5e790f8b38b07c120340b4eabc03f4d45fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
import os
import ipaddress
import math


def base(subnet4):
    return {
        "hooks-libraries": [
            {
                "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_flex_option.so",
                "parameters": {
                    "options": [
                        {
                            "name": "vendor-encapsulated-options",
                            "client-class": "fap-class",
                            "sub-options": [
                                    {
                                        "name": "config-file-name",
                                        "space": "vendor-encapsulated-options-space",
                                        "supersede": "ifelse(option[82].option[1].exists,concat('api/templates/magic.conf/a=', option[82].option[1].hex),'')"
                                    }
                            ]
                        },
                        {
                            "name": "host-name",
                            "client-class": "fap-class",
                            "remove": "option[12].exists"
                        }
                    ]
                }
            },
            {
                "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_run_script.so",
                "parameters": {
                    "name": "/etc/kea/gondul.sh",
                    "sync": False
                }
            }
        ],
        "dhcp-ddns": {
            "enable-updates": True,
            "server-ip": "::1",
        },
        "ddns-send-updates": True,
        "ddns-override-no-update": False,
        "ddns-override-client-update": False,
        "ddns-replace-client-name": "always",
        "ddns-generated-prefix": "dyn",
        "ddns-update-on-renew": False,
        "ddns-use-conflict-resolution": True,
        "interfaces-config": {
            "interfaces": [
                os.environ.get('DHCP_INTERFACE', 'eth0')
            ],
            "dhcp-socket-type": "udp"
        },
        "control-socket": {
            "socket-type": "unix",
            "socket-name": "/tmp/kea4-ctrl-socket"
        },
        "lease-database": {
            "type": "postgresql",
            "name": "kea",
            "user": "kea",
            "password": os.environ['DHCP_LEASE_DB_PASSWORD']
        },
        "expired-leases-processing": {
            "reclaim-timer-wait-time": 10,
            "flush-reclaimed-timer-wait-time": 25,
            "hold-reclaimed-time": 3600,
            "max-reclaim-leases": 100,
            "max-reclaim-time": 250,
            "unwarned-reclaim-cycles": 5
        },
        "authoritative": True,
        "renew-timer": 900,
        "rebind-timer": 1800,
        "valid-lifetime": 3600,
        "option-def": [
            {
                "name": "image-file-name",
                "code": 0,
                "space": "vendor-encapsulated-options-space",
                "type": "string",
                "array": False
            },
            {
                "name": "config-file-name",
                "code": 1,
                "space": "vendor-encapsulated-options-space",
                "type": "string",
                "array": False
            },
            {
                "name": "image-file-type",
                "code": 2,
                "space": "vendor-encapsulated-options-space",
                "type": "string",
                "array": False
            },
            {
                "name": "transfer-mode",
                "code": 3,
                "space": "vendor-encapsulated-options-space",
                "type": "string",
                "array": False
            },
            {
                "code": 150,
                "name": "tftp-server-address",
                "space": "dhcp4",
                "type": "ipv4-address",
                "array": True
            }
        ],
        "option-data": [
            {
                "name": "domain-name-servers",
                "data": os.environ['DOMAIN_NAME_SERVERS_V4']
            },
            {
                "name": "domain-name",
                "data": os.environ['DOMAIN_NAME']
            },
            {
                "name": "domain-search",
                "data": os.environ['DOMAIN_SEARCH']
            }
        ],
        "client-classes": [
            {
                "name": "client-juniper-vendor",
                "test": "substring(option[vendor-class-identifier].hex,0,7) == 'Juniper'"
            },
            {
                "name": "client-juniper-mac",
                "test": "substring(pkt4.mac, 0, 2) == '0x44f477' or substring(pkt4.mac, 0, 2) == '0xf01c2d'"
            },
            {
                "name": "fap-class",
                "test": "member('client-juniper-vendor') or member('client-juniper-mac')",
                "option-data": [
                    {
                        "name": "vendor-encapsulated-options",
                        "always-send": True
                    },
                    {
                        "name": "transfer-mode",
                        "space": "vendor-encapsulated-options-space",
                        "data": "http",
                        "always-send": True
                    },
                    {
                        "name": "tftp-server-address",
                        "data": os.environ['FAP_V4'],
                        "always-send": True
                    }
                ]
            },
            {
                "name": "Cisco-Phone",
                "test": "substring(option[60].hex,0,28) == 'Cisco Systems, Inc. IP Phone'",
                "option-data": [
                    {
                        "name": "tftp-server-address",
                        "data": os.environ['VOIP_V4'],
                        "always-send": True
                    },
                ],
            },
            {
                "name": "PXE-XClient_iPXE",
                "test": "substring(option[77].hex,0,4) == 'iPXE'",
                "boot-file-name": "https://{}/menu.ipxe".format(os.environ['NETBOOT_V4'])
            },
            {
                "name": "PXE-UEFI-32-1",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'",
                "next-server": os.environ['NETBOOT_V4'],
                "boot-file-name": "netboot.xyz.efi"
            },
            {
                "name": "PXE-UEFI-32-2",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002'",
                "next-server": os.environ['NETBOOT_V4'],
                "boot-file-name": "netboot.xyz.efi"
            },
            {
                "name": "PXE-UEFI-64-1",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007'",
                "next-server": os.environ['NETBOOT_V4'],
                "boot-file-name": "netboot.xyz.efi"
            },
            {
                "name": "PXE-UEFI-64-2",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00008'",
                "next-server": os.environ['NETBOOT_V4'],
                "boot-file-name": "netboot.xyz.efi"
            },
            {
                "name": "PXE-UEFI-64-3",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'",
                "next-server": os.environ['NETBOOT_V4'],
                "boot-file-name": "netboot.xyz.efi"
            },
            {
                "name": "PXE-Legacy",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'",
                "next-server": os.environ['NETBOOT_V4'],
                "boot-file-name": "netboot.xyz-undionly.kpxe"
            }
        ],
        "subnet4": subnet4,
        "loggers": [
            {
                "name": "kea-dhcp4",
                "output_options": [
                    {
                        "output": "/var/log/kea/dhcp4-debug.log",
                        "maxver": 8,
                        "maxsize": 204800,
                        "flush": True,
                        "pattern": "%d{%j %H:%M:%S.%q} %c %m\n"
                    }
                ],
                "severity": "DEBUG",
                "debuglevel": 40
            }
        ]
    }


def subnet(vlan, prefix, domain_name, vlan_domain_name):
    network = ipaddress.ip_network(prefix.prefix)
    gw, start_ip, end_ip = network[1], network[2], network[-2]
    
    return {
        "id": prefix.id,
        "subnet": prefix.prefix,
        "ddns-qualifying-suffix": vlan_domain_name,
        # Check if the VLAN in netbox has dhcp-ddns. This will enable full ddns using client hostnames.
        # Generate automatically using IP if not.
        "ddns-replace-client-name": "always" if not any(t['slug'] == 'dhcp-ddns' for t in vlan.tags) else "when-not-present",
        "pools": [
            {
                "pool": f"{start_ip} - {end_ip}"
            }
        ],
        "option-data": [
            {
                "name": "routers",
                "data": f"{gw}"
            },
            {
                "name": "domain-name",
                "data": f"{vlan_domain_name}, {domain_name}"
            },
            {
                "name": "domain-search",
                "data": f"{vlan_domain_name}, {domain_name}"
            }
        ],
        "user-context": {
            "name": vlan.name,
            "type": "clients"
        }
    }


def fap(vlan, prefix):
    network = ipaddress.ip_network(prefix.prefix)
    gw, start_ip, end_ip = network[1], network[(
        math.ceil(network.num_addresses - 50))], network[-2]

    return {
        "id": prefix.id,
        "client-class": "fap-class",
        "subnet": prefix.prefix,
        "pools": [
            {
                "pool": f"{start_ip} - {end_ip}"
            }
        ],
        "option-data": [
            {
                "name": "routers",
                "data": f"{gw}"
            }
        ],
        "user-context": {
            "name": vlan.name,
            "type": "fap"
        }
    }