servererstatning DB2DHCP (min gaffel), original her, som blir mer og mer vanskelig å montere for det nye operativsystemet. Og jeg liker ikke at det er en binær måte at det ikke er mulig å "endre akkurat nå"
skaffe en fungerende DHCP-server med muligheten til å velge abonnentens IP-adresse ved å bruke abonnentens mac eller bytte mac+port-kombinasjon (alternativ 82)
skrive en annen sykkel (Å, dette er favorittaktiviteten min)
motta kommentarer om klubbhendelsen din på Habrahabr (eller enda bedre, en invitasjon) 😉
Resultat: det fungerer 😉 Testet på FreeBSD og Ubuntu OS. Teoretisk sett kan koden bli bedt om å fungere under et hvilket som helst operativsystem, fordi Det ser ikke ut til å være noen spesifikke bindinger i koden.
Forsiktig! Det er mye mer i vente.
Link til repository for amatører "berør levende".
Prosessen med å installere, konfigurere og bruke resultatet av å "studere maskinvaren" er mye lavere, og deretter litt teori om DHCP-protokollen. For meg selv. Og for historien 😉
Litt teori
Hva er DHCP
Dette er en nettverksprotokoll som lar en enhet finne ut sin IP-adresse (og andre parametere som gateway, DNS, etc.) fra en DHCP-server. Pakker utveksles ved hjelp av UDP-protokollen. Det generelle prinsippet for drift av enheten når du ber om nettverksparametere er som følger:
Enheten (klienten) sender en UDP-kringkastingsforespørsel (DHCPDISCOVER) gjennom nettverket med forespørselen "vel, noen gir meg en IP-adresse." Dessuten skjer vanligvis (men ikke alltid) forespørselen fra port 68 (kilde), og destinasjonen er port 67 (destinasjon). Noen enheter sender også pakker fra port 67. MAC-adressen til klientenheten er inkludert i DHCPDISCOVER-pakken.
Alle DHCP-servere som ligger på nettverket (og det kan være flere av dem) danner et DHCPOFFER-tilbud med nettverksinnstillinger for enheten som sendte DHCPDISCOVER, og kringkaster det også over nettverket. Identifikasjon av hvem denne pakken er ment for er basert på MAC-adressen til klienten som ble oppgitt tidligere i DHCPDISCOVER-forespørselen.
Klienten godtar pakker med forslag til nettverksinnstillinger, velger den mest attraktive (kriteriene kan være forskjellige, for eksempel tidspunktet for pakkelevering, antall mellomliggende ruter), og gjør en "offisiell forespørsel" DHCPREQUEST med nettverksinnstillingene fra DHCP-serveren den liker. I dette tilfellet går pakken til en bestemt DHCP-server.
Serveren som mottok DHCPREQUEST sender en DHCPACK-formatpakke, der den igjen viser nettverksinnstillingene beregnet for denne klienten
I tillegg er det DHCPINFORM-pakker som kommer fra klienten, og formålet med disse er å informere DHCP-serveren om at "klienten er i live" og bruker de utstedte nettverksinnstillingene. I denne serverens implementering ignoreres disse pakkene.
Pakkeformat
Generelt ser en Ethernet-pakkeramme omtrent slik ut:
I vårt tilfelle vil vi kun vurdere dataene direkte fra innholdet i UDP-pakken, uten OSI-lagprotokolloverskrifter, nemlig DHCP-strukturen:
DHCPOPPDATER
Så prosessen med å skaffe en IP-adresse for en enhet begynner med at DHCP-klienten sender en kringkastingsforespørsel fra port 68 til 255.255.255.255:67. I denne pakken inkluderer klienten sin MAC-adresse, samt hva den nøyaktig ønsker å motta fra DHCP-serveren. Pakkestrukturen er beskrevet i tabellen nedenfor.
DHCPDISCOVER Pakkestrukturtabell
Plassering i pakken
Verdinavn
Eksempel
Представление
byte
Avklaring
1
Oppstartsforespørsel
1
Hex
1
Meldingstype. 1 - forespørsel fra klient til server, 2 - svar fra server til klient
2
Maskinvaretype
1
Hex
1
Type maskinvareadresse, i denne protokollen 1 - MAC
3
Lengde på maskinvareadresser
6
Hex
1
Lengde på enhetens MAC-adresse
4
Humle
1
Hex
1
Antall mellomveier
5
Transaksjons-ID
23:cf:de:1d
Hex
4
Unik transaksjonsidentifikator. Generert av klienten i begynnelsen av en forespørselsoperasjon
7
Andre gikk
0
Hex
4
Tid i sekunder fra begynnelsen av prosessen med å skaffe en adresse
9
Støvelflagg
0
Hex
2
Visse flagg som kan settes for å indikere protokollparametere
Alternativ 82, som overfører MAC-adressen til repeaterenheten og noen tilleggsverdier.
Oftest er dette porten til bryteren som slutt-DHCP-klienten kjører på. Dette alternativet inneholder tilleggsparametere. Den første byten er nummeret på "underalternativet", den andre er lengden, deretter verdien.
I dette tilfellet, i alternativ 82, er underalternativene nestet:
Agentkrets-ID = 00:04:00:01:00:04, der de to siste bytene er DHCP-klientporten som forespørselen kom fra
Agent Remote ID = 00:06:c8:be:19:93:11:48 - MAC-adressen til DHCP-repeaterenheten
Slutt på pakken
255
desember
1
255 symboliserer slutten av pakken
DHCP-TILBUD
Så snart serveren mottar DHCPDISCOVER-pakken og hvis den ser at den kan tilby klienten noe fra den forespurte, genererer den et svar for den - DHCPDISCOVER. Svaret sendes til havnen "der det kom fra", ved kringkasting, fordi for øyeblikket har klienten ennå ikke en IP-adresse, derfor kan den bare godta pakken hvis den sendes med kringkasting. Klienten gjenkjenner at dette er en pakke for ham ved hans MAC-adresse inne i pakken, samt transaksjonsnummeret som han genererer på det tidspunktet den første pakken opprettes.
DHCPOFFER Pakkestrukturtabell
Plassering i pakken
Navn på verdi (vanlig)
Eksempel
Представление
byte
Avklaring
1
Oppstartsforespørsel
1
Hex
1
Meldingstype. 1 - forespørsel fra klient til server, 2 - svar fra server til klient
2
Maskinvaretype
1
Hex
1
Type maskinvareadresse, i denne protokollen 1 - MAC
3
Lengde på maskinvareadresser
6
Hex
1
Lengde på enhetens MAC-adresse
4
Humle
1
Hex
1
Antall mellomveier
5
Transaksjons-ID
23:cf:de:1d
Hex
4
Unik transaksjonsidentifikator. Generert av klienten i begynnelsen av en forespørselsoperasjon
7
Andre gikk
0
Hex
4
Tid i sekunder fra begynnelsen av prosessen med å skaffe en adresse
9
Støvelflagg
0
Hex
2
Visse flagg som kan settes for å indikere protokollparametere. I dette tilfellet betyr 0 Unicast-forespørselstypen
15
Din klients IP-adresse
172.16.134.61
Linje
4
IP-adresse som tilbys av serveren (hvis tilgjengelig)
19
Neste server IP-adresse
0.0.0.0
Linje
4
Server IP-adresse (hvis kjent)
23
Reléagentens IP-adresse
172.16.114.41
Linje
4
IP-adressen til reléagenten (for eksempel en svitsj)
27
Klientens MAC-adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-adressen til pakkeavsenderen (klienten)
31
Klientmaskinvareadresseutfylling
Hex
10
Reservert sete. Vanligvis fylt med nuller
41
Serverens vertsnavn
Linje
64
DHCP-servernavn. Vanligvis ikke overført
105
Navn på oppstartsfil
Linje
128
Filnavn på serveren som brukes av diskløse stasjoner ved oppstart
235
Magisk kake
63: 82: 53: 63
Hex
4
"Magisk" nummer, ifølge hvilket, inkl. du kan fastslå at denne pakken tilhører DHCP-protokollen
DHCP-alternativer. Kan gå i hvilken som helst rekkefølge
236
Opsjonsnummer
53
desember
1
Alternativ 53, som definerer DHCP 2-pakketypen - DHCPOFFER
Alternativ lengde
1
desember
1
Opsjonsverdi
2
desember
1
Opsjonsnummer
1
desember
1
Mulighet for å tilby DHCP-klienten en nettverksmaske
Alternativ lengde
4
desember
1
Opsjonsverdi
255.255.224.0
Linje
4
Opsjonsnummer
3
desember
1
Mulighet for å tilby DHCP-klienten en standard gateway
Alternativ lengde
4
desember
1
Opsjonsverdi
172.16.12.1
Linje
4
Opsjonsnummer
6
desember
1
Mulighet for å tilby DHCP til DNS-klient
Alternativ lengde
4
desember
1
Opsjonsverdi
8.8.8.8
Linje
4
Opsjonsnummer
51
desember
1
Levetiden til utstedte nettverksparametere i sekunder, hvoretter DHCP-klienten må be om dem på nytt
Alternativ lengde
4
desember
1
Opsjonsverdi
86400
desember
4
Opsjonsnummer
82
desember
1
Alternativ 82, gjentar det som kom i DHCPDISCOVER
Alternativ lengde
18
desember
1
Opsjonsverdi
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
desember
18
Slutt på pakken
255
desember
1
255 symboliserer slutten av pakken
Forespørsel om DHC
Etter at klienten mottar DHCPOFFER, danner han en pakke som ber om nettverksparametere ikke til alle DHCP-servere på nettverket, men bare til én spesifikk, hvis DHCPOFFER-tilbud han "likte" best. "Like"-kriteriene kan være forskjellige og avhenge av klientens DHCP-implementering. Mottakeren av forespørselen angis ved å bruke MAC-adressen til DHCP-serveren. Dessuten kan en DHCPREQUEST-pakke sendes av klienten uten først å generere DHCPDISCOVER, hvis serverens IP-adresse allerede er oppnådd tidligere.
DHCPREQUEST Pakkestrukturtabell
Plassering i pakken
Navn på verdi (vanlig)
Eksempel
Представление
byte
Avklaring
1
Oppstartsforespørsel
1
Hex
1
Meldingstype. 1 - forespørsel fra klient til server, 2 - svar fra server til klient
2
Maskinvaretype
1
Hex
1
Type maskinvareadresse, i denne protokollen 1 - MAC
3
Lengde på maskinvareadresser
6
Hex
1
Lengde på enhetens MAC-adresse
4
Humle
1
Hex
1
Antall mellomveier
5
Transaksjons-ID
23:cf:de:1d
Hex
4
Unik transaksjonsidentifikator. Generert av klienten i begynnelsen av en forespørselsoperasjon
7
Andre gikk
0
Hex
4
Tid i sekunder fra begynnelsen av prosessen med å skaffe en adresse
9
Støvelflagg
8000
Hex
2
Visse flagg som kan settes for å indikere protokollparametere. I dette tilfellet er "kringkasting" satt
15
Din klients IP-adresse
172.16.134.61
Linje
4
IP-adresse som tilbys av serveren (hvis tilgjengelig)
19
Neste server IP-adresse
0.0.0.0
Linje
4
Server IP-adresse (hvis kjent)
23
Reléagentens IP-adresse
172.16.114.41
Linje
4
IP-adressen til reléagenten (for eksempel en svitsj)
27
Klientens MAC-adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-adressen til pakkeavsenderen (klienten)
31
Klientmaskinvareadresseutfylling
Hex
10
Reservert sete. Vanligvis fylt med nuller
41
Serverens vertsnavn
Linje
64
DHCP-servernavn. Vanligvis ikke overført
105
Navn på oppstartsfil
Linje
128
Filnavn på serveren som brukes av diskløse stasjoner ved oppstart
235
Magisk kake
63: 82: 53: 63
Hex
4
"Magisk" nummer, ifølge hvilket, inkl. du kan fastslå at denne pakken tilhører DHCP-protokollen
DHCP-alternativer. Kan gå i hvilken som helst rekkefølge
236
Opsjonsnummer
53
desember
3
Alternativ 53, som definerer DHCP-pakketype 3 - DHCPREQUEST
Alternativ lengde
1
desember
1
Opsjonsverdi
3
desember
1
Opsjonsnummer
61
desember
1
Klient-ID: 01 (for Ehernet) + klient-MAC-adresse
Alternativ lengde
7
desember
1
Opsjonsverdi
01:2c:ab:25:ff:72:a6
Hex
7
Opsjonsnummer
60
desember
"Leverandørklasseidentifikator". I mitt tilfelle rapporterer den DHCP-klientversjonen. Kanskje andre enheter returnerer noe annet. Windows rapporterer for eksempel MSFT 5.0
Alternativ lengde
11
desember
Opsjonsverdi
udhcp 0.9.8
Linje
Opsjonsnummer
55
1
Nettverksparametere forespurt av klienten. Sammensetningen kan variere
Opsjonsnummer
82
desember
1
Alternativ 82, gjentar det som kom i DHCPDISCOVER
Alternativ lengde
18
desember
1
Opsjonsverdi
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
desember
18
Slutt på pakken
255
desember
1
255 symboliserer slutten av pakken
DHCPACK
Som bekreftelse på at "ja, det stemmer, dette er din IP-adresse, og jeg vil ikke gi den ut til noen andre" fra DHCP-serveren, serverer en pakke i DHCPACK-format fra serveren til klienten. Det sendes kringkastet akkurat som andre pakker. Selv om jeg, i koden nedenfor for en DHCP-server implementert i Python, dupliserer enhver kringkastingsforespørsel ved å sende en pakke til en spesifikk klient-IP, hvis den allerede er kjent. Dessuten bryr ikke DHCP-serveren seg i det hele tatt om DHCPACK-pakken har nådd klienten. Hvis klienten ikke mottar DHCPACK, gjentar den ganske enkelt DHCPREQUEST etter en stund
DHCPACK-pakkestrukturtabell
Plassering i pakken
Navn på verdi (vanlig)
Eksempel
Представление
byte
Avklaring
1
Oppstartsforespørsel
2
Hex
1
Meldingstype. 1 - forespørsel fra klient til server, 2 - svar fra server til klient
2
Maskinvaretype
1
Hex
1
Type maskinvareadresse, i denne protokollen 1 - MAC
3
Lengde på maskinvareadresser
6
Hex
1
Lengde på enhetens MAC-adresse
4
Humle
1
Hex
1
Antall mellomveier
5
Transaksjons-ID
23:cf:de:1d
Hex
4
Unik transaksjonsidentifikator. Generert av klienten i begynnelsen av en forespørselsoperasjon
7
Andre gikk
0
Hex
4
Tid i sekunder fra begynnelsen av prosessen med å skaffe en adresse
9
Støvelflagg
8000
Hex
2
Visse flagg som kan settes for å indikere protokollparametere. I dette tilfellet er "kringkasting" satt
Vi lager en MySQL-database, laster opp pydhcp.sql-dumpen til den og konfigurerer konfigurasjonsfilen.
Konfigurasjon
Alle serverinnstillinger er i en xml-fil. Referansefil:
1.0 0.0.0.0 255.255.255.255 192.168.0.71 8600 1 255.255.255.0 192.168.0.1 lokal vert test test pydhcp option_8.8.8.8_hex:sw_port82:1:20 option_22_hex:sw_port82:2:16 option_18_hex:sw_mac:82:26 40 velg ip,mask,ruter,dns fra brukere der upper(mac)=upper('{option_3_AgentRemoteId_hex}') og upper(port)=upper('{option_1_AgentCircuitId_port_hex}') velg ip,maske,ruter,dns fra brukere der upper(mac)=upper('{sw_mac}') og upper(port)=upper('{sw_port82}') velg ip,maske,ruter,dns fra brukere der upper(mac)=upper('{ClientMacAddress}') sett inn verdier i historikk (id,dt,mac,ip,comment) (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')
Nå mer detaljert om taggene:
dhcpserver-delen beskriver de grunnleggende innstillingene for å starte serveren, nemlig:
vert - hvilken IP-adresse serveren lytter til på port 67
broadcast - hvilken ip er sendingen for DHCPOFFER og DHCPACK
DHCPServer - hva er IP-en til DHCP-serveren
LeaseTime leietid for den utstedte IP-adressen
ThreadLimit - hvor mange tråder kjører samtidig for å behandle innkommende UDP-pakker på port 67. Det er ment å hjelpe på høybelastningsprosjekter 😉
defaultMask,defaultRouter,defaultDNS - hva som tilbys abonnenten som standard hvis en IP blir funnet i databasen, men tilleggsparametere er ikke spesifisert for den
mysql delen:
vert, brukernavn, passord, basenavn - alt taler for seg selv. En omtrentlig databasestruktur er lagt ut på GitHub
Spørringsseksjon: forespørsler om å motta TILBUD/ACK er beskrevet her:
offer_count — antall linjer med forespørsler som returnerer et resultat som ip,mask,ruter,dns
offer_n — spørringsstreng. Hvis returen er tom, utfører du følgende tilbudsforespørsel
history_sql - en spørring som for eksempel skriver til "autorisasjonshistorikken" for en abonnent
Forespørsler kan inkludere alle variabler fra alternativseksjonen eller alternativer fra DHCP-protokollen.
Alternativer-delen. Det er her det blir mer interessant. Her kan vi lage variabler som vi kan bruke senere i spørringsdelen.
For eksempel:
option_82_hex:sw_port1:20:22
, tar denne kommandolinjen hele linjen som kom i DHCP-forespørselsalternativ 82, i hex-format, i området fra 20 til og med 22 byte og legger den i den nye variabelen sw_port1 (bytt port der forespørselen kom)
option_82_hex:sw_mac:26:40
, definer sw_mac-variabelen, og tar hex fra området 26:40
Du kan se alle mulige alternativer som kan brukes i spørringer ved å starte serveren med -d-bryteren. Vi vil se noe slikt:
Følgelig kan vi pakke hvilken som helst variabel i {} og den vil bli brukt i SQL-spørringen.
La oss registrere for historikk at klienten mottok IP-adressen:
Serverstart
./pydhcpdb.py -d -c config.xml
— d konsoll utgangsmodus DEBUG
- c <filnavn> konfigurasjonsfil
debriefing
Og nå flere detaljer om implementering av serveren i Python. Det er en smerte. Python ble lært i farten. Mange øyeblikk er laget i stilen "wow, på en eller annen måte fikk jeg det til å fungere." Ikke optimalisert i det hele tatt, og forlatt i denne formen hovedsakelig på grunn av liten erfaring med Python-utvikling. Jeg vil dvele ved de mest interessante aspektene ved serverimplementeringen i "kode".
XML-konfigurasjonsfil-parser
Standard Python-modul xml.dom brukes. Det virker enkelt, men under implementeringen var det en merkbar mangel på tydelig dokumentasjon og eksempler på nettverket ved bruk av denne modulen.
tree = minidom.parse(gconfig["config_file"]) mconfig=tree.getElementsByTagName("mysql") for elem i mconfig: gconfig["mysql_host"]=elem.getElementsByTagName("host")[0].firstChild. gconfig["mysql_username"]=elem.getElementsByTagName("brukernavn")[0].firstChild.data gconfig["mysql_password"]=elem.getElementsByTagName("passord")[0].firstChild.data gconfig_basenamesq]l =elem.getElementsByTagName("basename")[0].firstChild.data dconfig=tree.getElementsByTagName("dhcpserver") for elem i dconfig: gconfig["broadcast"]=elem.getElementsByTagName("broadcast")[0]. firstChild.data gconfig["dhcp_host"]=elem.getElementsByTagName("host")[0].firstChild.data gconfig["dhcp_LeaseTime"]=elem.getElementsByTagName("LeaseTime")[0].firstChild.data gconfig[" dhcp_ThreadLimit"]=int(elem.getElementsByTagName("ThreadLimit")[0].firstChild.data) gconfig["dhcp_Server"]=elem.getElementsByTagName("DHCPServer")[0].firstChild.data gconfig"]defaultMacskp_default =elem.getElementsByTagName("defaultMask")[0].firstChild.data gconfig["dhcp_defaultRouter"]=elem.getElementsByTagName("defaultRouter")[0].firstChild.data gconfig["dhcp_defaultDgetElement"] defaultDNS")[0].firstChild.data qconfig=tree.getElementsByTagName("query") for elem i qconfig: gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data for num in range(int(gconfig["offer_count"])): gconfig["offer_"+str(num+1)]=elem.getElementsByTagName("offer_"+str(num+1))[0].firstChild.data gconfig ["history_sql"]=elem.getElementsByTagName("history_sql")[0].firstChild.data options=tree.getElementsByTagName("options") for elem i alternativer: node=elem.getElementsByTagName("option") for alternativer i node : optionsMod.append(options.firstChild.data)
Multithreading
Merkelig nok er multithreading i Python implementert veldig tydelig og enkelt.
def PacketWork(data,addr): ... # implementering av å analysere den innkommende pakken og svare på den ... mens True: data, addr = udp_socket.recvfrom(1024) # venter på UDP-pakketråden = threading.Thread( target=PacketWork , args=(data,addr,)).start() # som det kom - vi starter den tidligere definerte PacketWork-funksjonen i bakgrunnen med parametere mens threading.active_count() >gconfig["dhcp_ThreadLimit"]: tid. sleep(1) # if the number Det er flere tråder som allerede kjører enn i innstillingene, vi venter til det er færre av dem
Motta/sende DHCP-pakke
For å avskjære UDP-pakker som kommer gjennom nettverkskortet, må du "heve" kontakten:
AF_INET - betyr at adresseformatet vil være IP: port. Det kan også være AF_UNIX - hvor adressen er gitt av filnavnet.
SOCK_DGRAM - betyr at vi ikke aksepterer en "råpakke", men en som allerede har gått gjennom brannmuren, og med en delvis trimmet pakke. De. vi mottar bare en UDP-pakke uten den "fysiske" komponenten i UDP-pakkeinnpakningen. Hvis du bruker SOCK_RAW-flagget, må du også analysere denne "wrapperen".
Å sende en pakke kan være som en kringkasting:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) #bytt kontakten til kringkastingsmodus rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))
, og til adressen "hvor pakken kom fra":
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # bytt kontakten til multilyttermodus rz=udp_socket.sendto(packetack, addr)
, der SOL_SOCKET betyr "protokollnivået" for innstillingsalternativer,
, SO_BROADCAST alternativet at hjelmpakken er "kringkastet"
,SO_REUSEADDR-alternativet bytter kontakten til "mange lyttere"-modus. I teorien er det unødvendig i dette tilfellet, men på en av FreeBSD-serverne jeg testet på, fungerte ikke koden uten dette alternativet.
Parsing av en DHCP-pakke
Det var her jeg virkelig likte Python. Det viser seg at ut av esken lar det deg være ganske fleksibel med bytekoden. Tillater det veldig enkelt å oversette til desimalverdier, strenger og hex - dvs. dette er det vi faktisk trenger for å forstå strukturen til pakken. Så, for eksempel, kan du få en rekke byte i HEX og bare byte: