Зміст

Mini FAQ

Q: Що потрібно від свіча, щоб запрацювала option 82?
A: Мати налаштований і працюючий dhcp relay з увімкненою option 82, dhcp snooping, що не пропускає клієнтські запити в обхід рілею, та ip source guard, що дропає пакети для айпішок, які не отримано крізь рілей.\\.

Q: На яких свічах має бути рілей?
A: На всіх - рівня доступу.

Q: У нас тут пів мережі на мильницях, це буде працювати? А можна тримати частину мережі на IP+MAC та іншу частину на option 82?
A: Сталася критична помилка - у вас недостатньо грошей для використання цього функціоналу.

Налаштування Ubilling та DHCP option 82

Для того, щоб усе запрацювало в базовому варіанті, нам потрібно виконати шість простих кроків:

1. Підмережу користувачів додано як:

ID Початкова ІР Остання ІР Мережа/CIDR Тип мережі
1 172.16.0.0 172.16.0.255 172.16.0.0/24 dhcp82

2. /etc/rc.conf виглядає наступним чином

rc.conf
ifconfig_em0="inet 172.16.0.1 netmask 255.255.255.0"
ifconfig_em0_alias0="inet 172.32.0.1 netmask 255.255.240.0"
ifconfig_em0_alias1="inet 192.168.94.1 netmask 255.255.255.0"

3. Шаблон config/dhcp/global.template

global.template
option domain-name "ourisp";
option domain-name-servers 172.16.0.1;
default-lease-time 3600;
max-lease-time 43200;
authoritative;
ddns-update-style none;
log-facility local7;
one-lease-per-client true;
deny duplicates;
 
shared-network ourisp {
 
{SUBNETS}
 
subnet 192.168.94.0 netmask 255.255.255.0 {
}
 
subnet 172.32.0.0 netmask 255.255.240.0 {
  default-lease-time 3600;
  option domain-name "isp";
  option subnet-mask 255.255.240.0;
  option domain-name-servers 172.32.0.1;
  option routers 172.32.0.1;
 
  pool {
   range 172.32.0.100 172.32.0.254;
    {DENYMEMBERS}
   }
 
log(info, "==");
if exists agent.remote-id and exists agent.circuit-id {
if binary-to-ascii(16, 8, "", substring(option agent.remote-id, 2, 1)) = "0" {
set switch-mac = concat("0", binary-to-ascii(16, 8, "", substring(option agent.remote-id, 2, 1)), ":", binary-to-ascii(16, 8, ":", substring(option agent.remote-id, 3, 6)));
# log(info,concat("SWITCH-MAC1:",switch-mac));
} else {
# set switch-mac = binary-to-ascii(16, 8, ":", substring(option agent.remote-id, 2, 6));
# log(info,concat("SWITCH-MAC2:",switch-mac));
set switch-mac = concat (
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(option agent.remote-id,2,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(option agent.remote-id,3,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(option agent.remote-id,4,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(option agent.remote-id,5,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(option agent.remote-id,6,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(option agent.remote-id,7,1))),2)
);
# log(info,concat("SWITCH-MAC3:",switch-mac3));
}
set clip = binary-to-ascii(10,8,".",leased-address);
set clremote = binary-to-ascii(16,8,"",option agent.remote-id);
set clcircuit = binary-to-ascii(10,8,"",option agent.circuit-id);
set switch-port = binary-to-ascii(10, 8, "", substring(option agent.circuit-id, 5, 1));
set switch-port-vlan = binary-to-ascii(10, 16, "", substring(option agent.circuit-id, 2, 2));
 
log( info,concat("OPTION82 INFO - SWITCHMAC: ",switch-mac," PORTSW: ",switch-port," VLAN: ",switch-port-vlan));
 
log( info,concat("*Leased IP: ",clip, " SWITCH: ",clremote," PORT: ",clcircuit," SWITCHMAC: ",switch-mac," PORTSW: ",switch-port," VLAN: ",switch-port-vlan," (with opt82)") );
} else {
set clhw = concat (
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
);
 
log( info,concat("*Leased IP: ",binary-to-ascii(10,8,".",leased-address), " MAC: ", clhw," (without opt82)") );
}
log(info, "==");
 
}
 
}

4. Шаблон config/dhcp/option82.template перебуває в дефолтному стані (підтримувані макроси)

5. У конфізі alter.ini встановлено опцію NMLEASES = /var/log/dhcpd.log і налаштовано правильне логування ISP-DHCPD

6. В OnConnect вимкнено прибивання за MAC-у. Тепер це проблема ip source guard і dhcp snooping вашого свіча (192.168.94.4 у прикладі).

Виходячи з усього вищевказаного, не складно зробити висновок, що прив'язка планується за парою повних remote-id/circuit-id. Перевірити поведінку такого конфіга не складно за допомогою наступних нехитрих рухів тіла

Відлагодження scapy

p="\x01\x01\x05\x02\x06\x11\x22\x34\x44\x55\x66"
dhcp_discover =  Ether(src=RandMAC(),dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=RandString(12,'0123456789abcdef'))/DHCP(options=[("message-type","discover"),("relay_agent_Information",p),"end"])
sendp(dhcp_discover)

у відповідь на що ми маємо отримати щось на кшталт:

dhcpd.log
Oct 15 00:38:20 test dhcpd: Wrote 0 class decls to leases file.
Oct 15 00:38:20 test dhcpd: Wrote 1 leases to leases file.
Oct 15 00:38:34 test dhcpd: ==
Oct 15 00:38:34 test dhcpd: *Leased IP: 172.16.0.2 SWITCH: 112234445566 PORT: 5 (with opt82)
Oct 15 00:38:34 test dhcpd: ==
Oct 15 00:38:34 test dhcpd: ==
Oct 15 00:38:34 test dhcpd: *Leased IP: 172.16.0.2 SWITCH: 112234445566 PORT: 5 (with opt82)
Oct 15 00:38:34 test dhcpd: ==
Oct 15 00:38:34 test dhcpd: DHCPDISCOVER from 30:32:63:30:35:62 via em0
Oct 15 00:38:35 test dhcpd: DHCPOFFER on 172.16.0.2 to 30:32:63:30:35:62 via em0

а також під час прослуховування за допомогою

tcpdump -n -i em0 -s 0 -v -vv

ми спостерігатимемо прилітаючі до нас

Agent-Information Option 82, length 13: 
              Remote-ID........
	      Circuit-ID.......

Особливості роботи на комутаторах Zyxel

GS-4012, GS-3012 взагалі не повертають remote-id. Тому орієнтуватися варто тільки на sub-option 1 (Agent Circuit ID). Тож при побудові шаблону потрібно керуватися ось цим: http://zyxel.ru/kb/2030

У чорновому варіанті можна оформити це приблизно так:

    set clremote = binary-to-ascii(10,16,"",substring(option agent.circuit-id,4,2));
    set clcircuit = binary-to-ascii(10,16,"",substring(option agent.circuit-id,1,2));

а також виправити option82.template відповідно до цих реалій

що в результаті дає нам такий лог

Oct 15 16:52:00 test dhcpd: ==
Oct 15 16:52:00 test dhcpd: *Leased IP: 172.32.0.100 SWITCH: 18259 PORT: 768 (with opt82)
Oct 15 16:52:00 test dhcpd: ==

Приклад конфігурації Zyxel GS-3012:

ip address inband-default 192.168.94.4 255.255.255.0 
ip address default-gateway 192.168.94.1
interface port-channel 12  
  dhcp snooping trust 
exit 
dhcp smart-relay 
dhcp smart-relay helper-address 192.168.94.1 
dhcp smart-relay option 
dhcp smart-relay information 
dhcp snooping 
dhcp snooping vlan 1  
dhcp snooping vlan 1 option 
dhcp snooping vlan 1 information 
dhcp dhcp-vlan 1 

Спрощення життя

Для зручнішого виловлювання пар remote-id і circuit-id існує сервіс аналогічний UHW, що знаходиться в docs/opt82_uhw. Налаштування в основному аналогічне такому в оригінального сервісу. Ходять чутки, що погрожували дописати ще й самостійну активацію абонентом ;)

Патч для isc-dhcp44-server

Для того, щоб користувачі могли змінювати свої пристрої скільки завгодно разів, і не чекати, поки на сервері закінчиться ліза під їхню адресу, - до DHCP-сервера потрібно застосувати патч і перезібрати пакет із вихідного коду.

--- server/dhcp.c.orig	2017-07-25 16:39:54.000000000 +0300
+++ server/dhcp.c	2017-09-13 00:26:29.330284000 +0300
@@ -31,6 +31,7 @@
 #include <limits.h>
 #include <sys/time.h>
 
+extern int flag_dd_option;
 static void maybe_return_agent_options(struct packet *packet,
 				       struct option_state *options);
 static int reuse_lease (struct packet* packet, struct lease* new_lease,
@@ -4900,8 +4901,13 @@
 		{
 			if (LEASE_NOT_EMPTY(pool->free))
 				candl = LEASE_GET_FIRST(pool->free);
-			else
+			else if (LEASE_NOT_EMPTY(pool->abandoned))
 				candl = LEASE_GET_FIRST(pool->abandoned);
+			else if (flag_dd_option)
+			{
+				candl = LEASE_GET_FIRST(pool->active);
+				candl -> ends = cur_time;
+			}
 		}
 
 		/*
--- server/dhcpd.c.orig	2017-07-25 16:39:54.000000000 +0300
+++ server/dhcpd.c	2017-09-13 03:25:44.556962000 +0300
@@ -57,6 +57,7 @@
 gid_t set_gid = 0;
 #endif /* PARANOIA */
 
+int flag_dd_option = 0;
 struct iaddr server_identifier;
 int server_identifier_matched;
 
@@ -184,6 +185,7 @@
 		  "             [-play trace-input-file]\n"
 #endif /* TRACING */
 		  "             [-pf pid-file] [--no-pid] [-s server]\n"
+		  "             [-dd] - enable dd mode\n"
 		  "             [if0 [...ifN]]",
 		  isc_file_basename(progname));
 }
@@ -329,6 +331,8 @@
 				usage(use_noarg, argv[i-1]);
 			set_chroot = argv [i];
 #endif /* PARANOIA */
+		} else if (!strcmp (argv [i], "-dd")) {
+			flag_dd_option = 1;
 		} else if (!strcmp (argv [i], "-cf")) {
 			if (++i == argc)
 				usage(use_noarg, argv[i-1]);

Після того, як перезібрали пакет та встановили його в системі, необхідно через rc.conf ввімкнути даний функціонал, додавши опцію -dd до параметрів запуску, наприклад:

rc.conf
dhcpd_flags="-q -dd"