Nettradio og MP3-spiller med Raspberry Pico

 20 total views,  1 views today

Ved hjelp av et kretskort som dekoder MP3 til lydsignaler kan vi lage en MP3-spiller eller en Internet-radio. Alt som trengs er litt programmering i Rasberry Pico. Her brukes programmeringsspråket MicroPython.

Anders Fongen, mai 2023

Lyd lagret som MP3 må dekodes før det kan sendes som lydsignaler til en høyttaler. En slik dekoder kan lages i programvare, dersom prosessoren er kraftig nok. Dette går greit i en PC, men en mikrokontroller (f.eks. Arduino, EPS8266, Raspberry Pico) kan ikke påregnes å ha tilstrekkelig datakraft for dette formålet.

Det finnes egne integrerte kretser som utfører MP3-dekoding i maskinvaren, uavhengig av prosessorkraften i kontrolleren, og som lar oss lage en MP3-spiller med billige og strømsparende komponenter. Denne bloggartikkelen vil vise hvordan slike komponenter koples sammen og hvordan programmeringen gjøres.

Komponenter som kan brukes

Mange komponenter kan brukes for et eksperiment likt det som presenteres her. De valgte komponentene er en Raspberry Pico (heretter kalt RPico) og en dekoderkrets kalt VS1053 fra VLSI Solutions.

Oppsett og programmering av RPico blir ikke gjennomgått her, det finnes mange hjelperessurser for dette formålet på nettet. Her vil det vises hvordan RPico og VS1053 koples sammen, og den programmeringskoden som kjører i RPico for å oppnå ønsket virkemåte.

En VS1053 er kun en liten brikke/chip, så for eksperimentformål er det vanlig å bruke et kretskort som også inkluderer støttekretser og tilkoplingspunkter. Som vist på bildet under er det utstyrt med koplingspinner som passer ned i et vanlig prototyp-brett (“breadboard”).

Et eksperimentkort for VS1053

VS1053 er en ganske avansert brikke som i tillegg til å dekode MP3 også dekoder lyd kodet med WAV, WMA, OGG, eller MIDI. Den kan ta opp lyd gjennom en mikrofonport (evt. linjeport) og kode den til OGG m.m. (dog ikke til MP3). Kretskortet som er brukt i denne artikkelen har også en SD-kortleser.

For å ta kretskortet i bruk koples en høyttaler til den ene minijack-porten, mens tilkoplingspinnene koples til pinner på RPico. Gjennom disse ledningene går det signaler for å konfigurere VS1053 for det foreliggende formålet, og for å overføre digitale lydsignaler som skal dekodes.

I ledningene mellom VS1053 og RPico benyttes en overføringsmetode som kalles Serial Peripheral Interface (SPI). Dette er en såkalt seriell metode hvor én og en bit overføres i høyt tempo over en enkel ledning. Fordelen med å benytte en “standardisert” metode er at den kan styres fra maskinvaren slik at det krever mindre programmering, og belaster hovedprosessoren mindre. Dessuten er SPI utformet slik at kontrolleren kan snakke med flere enheter gjennom samme ledning (som vi da kaller en buss). I dette tilfellet kan både SD-kortleseren og dekoderen bruke samme ledningssett.

Ledningsnettet

Tegningen nedenfor viser hvordan RPico og VS1053 kan koples sammen. Her er det mange valgmuligheter, men det må være samsvar mellom hvilke pinner på kontrolleren som brukes, og programvaren som skal styre dem. Det er altså mulig å velge andre pinner på RPico enn hva som foreslås her.

Tilkoplingspunkter på VS1053 og RPico

Bildet over viser navnene som er gitt til pinnene på VS1053. De avviker noe fra de navnene som brukes på databladet, så her kreves litt utprøving for å fortstå hvilke funksjoner de har. Her er fasiten:

VS1053 påtrykkBeskrivelseKoples til
RPico pin
Navn på
RPico
5VDriftsspenning40VBUS
GNDJordforbindelse38GND
CSChip select for SD-kortleseren2GPIO 1
MISOSPI datalinje fra VS10536GPIO 4
SISPI datalinje til VS10535GPIO 3
SCKSPI klokkesignal4GPIO 2
XCSChip select for MP3-dekoderen9GPIO 6
XRESHard reset (aktiv lav)1GPIO 0
XDCSData select (RPico->VS1053)10GPIO 7
DREQData request (VS1053->RPico)11GPIO 8
Sammenkoplingen av VS1053 og RPico

Merk at pinnene CS, XCS, XRES, XDCS er alle “aktiv lav”, dvs. at de skal ha 0 volt for å gi effekt. Dette fremgår også fra programkoden.

Bildet over til høyre viser bruken av en flatkabel med ledninger i forskjellige farger for å gjøre denne sammenkoplingen. Det er lett å gjøre feil i denne prosessen, og fargene var til stor hjelp. Et bilde over pinnene på RPico vises på bildet under. Legg merke til at pinnene MISO, SI og SCK er koplet til RPico på pinnene 4-6, som alle er benevnet SPI0. Om andre pinner brukes til dette formålet er det nødvendig at de koples til samme SPI-kanal (0 eller 1) med betegnelsene RX, TX og SCK.

Kommunikasjon med VS1053

RPico kommuniserer med VS1053 på en av fire måter:

  1. Skriv til et register. Verdiene i registrene konfigurerer VS1053. Programkoden viser hvilke verdier som er nødvendig for eksperimentet i denne artikkelen. Full oversikt over registerverdier finnes i databladet.
  2. Les verdien av et register. Ikke strengt nødvendig i denne artikkelen, men nyttig for å kontrollere at en skriveoperasjon er vellykket.
  3. Sende data. I denne artikkelen vil vi sende MP3-kodet lyd til dekoderen, og det skjer på denne måten.
  4. Motta data. VS1053 kan også kode lyd til digital form, som da kan mottas av RPico. Ikke brukt i denne artikkelen.

Skrive til et register

For å skrive en registerverdi må RPico gjøre følgende:

  • Sette XCS=0, XDCS=1
  • Vente til DREQ==1
  • Bygge en 4 byte streng som starter på x02, deretter fulgt av adressen til registeret, og to bytes med den 16-bits verdien som skal skrives dit.
  • Sende strengen til SPI-kanalen
  • Sette XCS=1

Her ser du Python-koden for funksjonen write_command:

    def write_command(self, address_byte, data_byte1, data_byte2):
        # Set the control pins to indicate a command
        self.xcs.value(0)
        self.xdcs.value(1)

        while not self.dreq.value():
            pass

        # Send the address and data bytes over SPI
        ba = (bytearray([0x02, address_byte, data_byte1, data_byte2]))
        self.spi.write(ba)

        # Turn off XCS signals
        self.xcs.value(1)

Lese en registerverdi

For å lese verdien lagret i et register må RPico gjøre dette:

  • Sette XCS=0, XDCS=1
  • Vente til DREQ==1
  • Bygge en 2 byte streng som starter på x03, deretter følger adressen til registeret.
  • Sende strengen til SPI-kanalen
  • Lese to bytes fra SPI-kanalen, som inneholder verdien av registeret.

Slik:

    def read_register(self, address_byte):
        # Set the control pins to indicate a command
        self.xcs.value(0)
        self.xdcs.value(1)
        while not self.dreq.value():
            pass
        self.spi.write(bytearray([0x03, address_byte]))

        resp = self.spi.read(2)

        self.xcs.value(1)
        return resp

Sende data

Disse stegene er nødvendige:

  • Sette XCS=1, XDCS=0
  • Vente til DREQ==1
  • Sende maksimalt 32 bytes til SPI-kanalen
  • Sette XDCS=1

Slik:

    def write_data(self, data):
        # Set the control pins to indicate data
        self.xcs.value(1)
        self.xdcs.value(0)

        while not self.dreq.value():
            pass
        self.spi.write(data)
        self.xdcs.value(1)

Nødvendige deklarasjoner

Noen programsetninger er nødvendige for å deklarere og initialisere portene, samt importere noen nødvendige biblioteker:

from machine import Pin, SPI
import time
spi_mosi = Pin(3)
spi_miso = Pin(4)
spi_sck = Pin(2)

# Define the pins for the VS1053 module
vs1053_xcs = Pin(6)
vs1053_xdcs = Pin(7)
vs1053_dreq = Pin(8)
vs1053_reset = Pin(0)

# Initialize the SPI bus for the mp3 decoder 
co_spi = SPI(0, baudrate=1000000, polarity=0, phase=0, sck=spi_sck,\\
   mosi=spi_mosi, miso=spi_miso)

Jeg har valgt å legge all håndteringen av VS1053 inn som en klasse i Python, og den blir initialisert med denne koden:

class VS1053:
    def __init__(self, spi, xcs, xdcs, dreq, reset):
        self.xcs = xcs
        self.xdcs = xdcs
        self.dreq = dreq
        self.reset = reset
        self.spi = spi

    def init(self):
        self.xcs.init(Pin.OUT, value=1)
        self.xdcs.init(Pin.OUT, value=1)
        self.dreq.init(Pin.IN)
        self.reset.init(Pin.OUT, value=1)

        # Reset the VS1053 module
        self.reset(vs1053_reset)
        self.write_command(0x0, 0x8, 0x4)
        self.write_command(0x3, 0xe0, 0)    # Clock multiplier
        self.write_command(0xb, 0x30, 0x30) # Volume control, 0-loudest	

    def reset(self,pin_reset): # Hardware reset of VS1053
        self.reset.value(0)
        time.sleep_ms(1)
        self.reset.value(1)

Utover det å opprette instansvariabler, vil koden initialisere GPIO-portene med riktig tilstand, resette VS1053 og så skrive inn noen nødvendige registerverdier. Se seksjon 9.6 i databladet for en full beskrivelse av disse.

Full operasjon av VS1053

Etter konfigurasjon slik som beskrevet over og med de funksjonene for lesing og skriving til VS1053 kan vi nå finne frem MP3-data for å teste at vi får avspilt lyd. MP3-data kan finnes fra mange kilder, men i denne artikkelen vil to muligheter belyses nærmere: (1) Hente MP3-filer fra et SD-minnekort, dvs. filer som du har kjøpt på nettet eller hentet fra CD-plater (kalt “ripping”). (2) Hente MP3-data fra nettet med såkalt “strømming”, som innebærer at lyden spilles av etter hvert som de hentes. Begge alternativer vil bli demonstrert, men metoden med å hente data fra nettet er den enkleste og vil bli vist først.

Internet radio

Raspberry Pico finnes i en “W”-versjon (kalt RPicoW heretter), og den har kretser som kan koples til et trådløst nett og videre til Internet. Med denne versjonen brukt i dette eksperimentet er det relativt enkelt å bruke denne muligheten til å spille av radiostasjoner på Internet.

Her viser vi først programsetningene for å kople RPicoW til et trådløst nett (connect_wifi). Vi trenger som vanlig SSID (nettets navn) og et passord som må legges inn i programmet. Funksjonen connect setter opp en forbindelse til en web-tjener. For det formålet trenger vi en URL som inneholder web -tjenerens navn og angivelse av den MP3-strømmen som ønsket mottatt. MicroPython inneholder ikke ferdiglaget kode for HTTP-protokollen, så funksjonen inneholder også noen kodesetninger som starter den faktiske dataoverføringen.

class netradio:
    def connect_wifi(self,ssid,password):
        self.sta_if = network.WLAN(network.STA_IF)
        self.sta_if.active(True)
        self.sta_if.connect(ssid,password)
        while not self.sta_if.isconnected():
            time.sleep_ms(500)

    def connect(self,radio_IP,radio_URL,tcp_port=80):
        self.sock = socket.socket()
        addr = socket.getaddrinfo(radioIP, tcp_port)[0][-1]
        self.sock.connect(addr)
        request = "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" % (radioURL,radioIP)
        self.sock.send(request)
        return self.sock
    
    def read_data(self,numbytes):
        return sock.recv(numbytes)

Funksjonen read_data returnerer en såkalt socket, som er en “brønn” som vi kan øse data fra. Den kan brukes som en parameter i VS1053-klassens funksjoner for å hente MP3-data i 32-bytes porsjoner og sende til dekoderen med funksjonen write_data. Funksjonen for å oppnå dette ligger i VS1053-klassen og kalles play_stream. Koden ser slik ut:

    def play_stream(self,socket):
        while True:
            soundpacket = socket.recv(32)
            if soundpacket:
                self.write_data(soundpacket)
            else:
                break
        socket.close()

Hva vi nå trenger for å starte nettradioen er en høyttaler koplet til minijack-utgangen på VS1053, og følgende initialiseringskode i RPicoW:

# Initialize the VS1053 module
vs1053 = VS1053(co_spi, vs1053_xcs, vs1053_xdcs, vs1053_dreq, vs1053_reset)
vs1053.init()

# Start netradio
radioIP = "lyd.nrk.no"
radioURL= "/nrk_radio_klassisk_mp3_h?_hdr=0"

nradio = netradio()
nradio.connect_wifi("WiFi-navn","WiFi-passord")
vs1053.play_stream(nradio.connect(radioIP,radioURL))

Avspilling av MP3 fra SD-kort

Dette kretskortet med VS1053 inneholder også en SD kortleser (ikke alle gjør det), og en tilkoplet RPico (trenger ikke være en RPicoW) kan lese og skrive data til et minnekort. Minnekortet blir en del av filsystemet til RPico, og data leses og skrives gjennom ordinære metoder for filbehandling.

Tilkoplingen av en kortleser bruker også SPI-grensesnittet, og er litt mer omstendelig enn WiFi, så denne delen er spart til slutt, da selve dekoderen nå er testet og kjent.

Kortleseren bruker de samme ledningene til SPI-kommunikasjonen, men trenger sitt eget Chip Select signal. Den finner vi på pinnen CS på kretskortet, og koples til pin 2 (GPIO 1) på RPico. Se illustrasjonen under som viser hvordan SPI-ledningene kan deles av flere tilkoplede enheter.

Deling av SPI-buss på flere enheter. Merk av de har separate SS (Chip select) linjer

Kanaler på samme SPI-buss må initialiseres hver for seg, men en ny deklarasjon trenger ingen parametre når den bruker det samme bussnummeret. Altså slik:

import sdcard, os
# Initialize the SPI bus for the mp3 decoder and the sd card reader
co_spi = SPI(0, baudrate=1000000, polarity=0, phase=0, sck=spi_sck, \\
     mosi=spi_mosi, miso=spi_miso)
sd_spi = SPI(0)

# Chip select for SD card reader
sd_cs = Pin(1, Pin.OUT, value=1)

# Init SD card reader interface
card = sdcard.SDCard(sd_spi, sd_cs)
os.mount(card, '/sd')
print(os.listdir('/sd')) # Prints the files on the root directory

Modulen sdcard blir importert, dette er koden for å styre lesing og skriving på SD-kortet. Kildekoden kan lastes ned via lenken nedenfor, og må plasseres i filsystemet på RPico, f.eks. på /lib-katalogen.

Koden vist ovenfor gjør at filene på SD-kortet er gjort tilgjengelig fra katalogen /sd. Filer med MP3-innhold kan nå spilles av med en ny funksjon som vi legger til VS1053-klassen. Slik:

def play_file(self, filename):
        # Open the file and read the data
        chunk = 8192
        with open(filename, "rb") as f:
            data = f.read(chunk)

            # Loop through the file and send the data to the VS1053
            while data:
                idx = 0
                while True:
                    if idx+32 > len(data):
                        self.write_data(data[idx:])
                        break
                    else:
                        self.write_data(data[idx:idx+32])
                        idx += 32
                data = f.read(chunk)
    

Koden i funksjonen over kan trenge en forklaring: Her leses MP3-dataene i porsjoner på 8192 bytes, som deles opp i pakker på 32 bytes som sendes til VS1053 med write_data-funksjonen. Lesing av 8192 bytes tar mindre tid enn det å spille av 32 bytes, så det oppstår ikke hakking i lyden, noe som skjer dersom dette tallet settes mye høyere. Avspilling av filen /sd/andrea.mp3 kan nå skje med programsetningen

vs1053.play_file("/sd/andrea.mp3")

Brukergrensesnitt, sier du?

Denne artikkelen har som mål å vise hvilken Python-kode som kreves for å spille MP3-data fra nettet eller fra et SD-kort. Dette kan være til hjelp for dem som kjenner til MicroPython-programmering og kan inkludere disse programsetningene i sin egen kode. Den viste programkoden kan i sin helhet lastes ned herfra:

(Filene lastes ned med .pyx på slutten av filnavnet. Endre navnet til .py før bruk)

Skal denne koden brukes i egne programmer bør disse funksjonene legges til:

  1. Feilhåndtering. Alt som kan gå galt, vil trenge kode som fanger opp og behandler feilsituasjonen.
  2. Brukergrensesnitt. Start/stopp/pause i avspilling. Valg av katalog eller spilleliste for serieavspilling. Visning av tittel i et display m.m.

Anders håper at denne artikkelen kan være til nytte, skriv gjerne en kommentar om feil og mulige forbedringer.

Communication between DMR and a computer network

 19 total views,  1 views today

Anders Fongen, November 2022

Abstract: The integration of a sensor network with services from voice communication, text messaging and Global Positioning System (GPS) creates opportunities for improved situational awareness, better safety for field operators, higher confidence in sensor readings and improved return on equipment investment. Digital Mobile Radio (DMR) has been the choice of communication technology for a series of experiments where these potentials have been investigated. Additional technology components used were mostly inexpensive and open source.

The article was published in the The Sixteenth International Conference on Sensor Technologies and Applications (SENSORCOMM 2022) October 2022, Lisbon Portugal.

Demonstration videos

Below are two demonstration videos from the integration experiment, view these if you are not into reading academic papers:

The first video shows how to integrate a DMR radio into an pub-sub network using the MQTT protocol.

Demonstration video from the integration experiment – pubsub intergation

The second video shows how two MMDVM units can provide routing of IP packets across a DMR radio link.

Demonstration video from the integration experiment – IP routing

Full text of article

Download source code

This link allows you to download the source code in the form of a compressed tar file. Please observe that this is experimental code not meant for production, and:

  • The module gwtest.py is the root module, and the execution starts there
  • You will need to import the module dmr_utils3, paho-mqtt, netifaces, etc.
  • You will need to inspect the source code and modify values for ip-addresses, DMR-ids, UDP port number, MQTT topics etc.
  • You need to configure pi-star so that it connects to the Controller at its IP-address.
  • The code also includes code to route IP-packets over a DMR link. Un-comment this code if necessary, but this will require the program to run in root mode (sudo…)
  • If you make improvements and want to share them with me, you are welcome to contact me. Please accept that I cannot spend much time on advice and support otherwise.

Docker – nettverk og svermer

 18 total views

Anders Fongen, september 2022

Dette er et blogginnlegg som skal diskutere nettverk av Docker-kontainere, hvordan de kan kommunisere gjennom beskyttede interne nett, og hvordan de kan opptre i svermer under styring av en sentral kontrollnode. Forutsetningen for å få godt ubytte av denne teksten er grunnleggende kjennskap til bygging og kjøring av vanlige Docker-komponenter (eng. images)

Vi kan enkelt tenke oss fornuftige anvendelser hvor en Docker-komponent har nytte av å påkalle tjenester fra en annen komponent. Dette er da typisk en komponent som ikke betjenes med en HTML-basert web -dialog, men som benytter maskin-til-maskin kommunikasjon (m2m). Slik kommunikasjon kan gjerne være basert på HTTP-protokoll og benytte programmeringsbiblioteker for dette, men vil ha innholdet kodet med XML eller JSON. Slike komponenter forbinder vi med begrepet Service Oriented Architecture (SOA). Jeg kommer ikke til å komme inn på slik programmering her, men det finnes mengder av læremidler der ute basert på ditt favoritt programmeringsspråk.

Beskyttet nettverk internt i Docker

Når en komponent ønsker å kalle en annen, er det selvfølgelig mulig å utplassere den kalte komponenten slik at den kan kalles fra “utsiden” (evt. hele Internet) og bruke denne utvendige IP-adressen som servicepunkt. Det er lett å tenke seg hvorfor dette ofte ikke er ønskelig: Uvedkommende kan fritt utnytte og sabotere tjenesten med mindre man lager mekanismer for adgangskontroll, noe som strengt tatt ikke burde vært nødvendig.

Docker tilbyr derimot “interne” nettverk som kun er synlig for Docker-kontainere på den samme maskinen og som er koplet til det interne nettverket. I det man skaper et slikt nettverk:
$ docker network create my-internal-network
$ docker network ls
$ docker network inspect my-internal-network

vil det også bli tildelt en subnett-adresse, og forsynes med tjenester for tildeling av adresser fra subnettet (lik DHCP) og tjenester for å finne IP-adresser til de tilkoplede kontainerne (lik DNS). For siden å kople en kontainer til nettverket kan vi velge to metoder:

  1. Kople til nettverket i det kontaineren skapes, f.eks slik:
    $ docker run -p 80:80 --name mycontainer --net my-internal-network image-name

    Med denne metoden vil kontaineren mycontainer være knyttet til kun my-internal-network. Om kontaineren skal være knyttet til flere nettverk, bruk metode nr.2:
  2. Lage kontaineren, kople til nettverket og starte programmet, i tre steg (eksempelvis)
    $ docker create -p 80:80 -name mycontainer image-name
    $ docker network connect my-internal-network mycontainer
    $ docker network connect my-second-internal-network mycontainer
    $ docker network disconnect bridge mycontainer
    $ docker start mycontainer

    Denne listen av kommandoen viser bruken av create, som oppretter en kontainer uten å starte den, network connect som kopler kontaineren til interne nettverk, og disconnect, som kopler fra nettverk. Deretter starter kjøringen av programmet i kontaineren med $docker run.

Det forholder seg nemlig slik at en kontainer som standardinnstilling koples til et nettverk kalt bridge. Alle andre kontainere vil også ha forbindelse til denne, noe som i mange tilfeller ikke er ønskelig. Metode nr.1 vil erstatte bridge-forbindelsen med en forbindelse til my-internal-network. Metode nr.2 vil legge nye forbindelser til eksisterende.

Eksempel 1 – BusyBox

Som eksempel på viste metode nr.2 kan komponenten BusyBox brukes. Den gir et lite Linux-kjøremiljø som vi kan skrive kommandoer til.

Først lister vi opp eksisterende nettverk og skaper to nye:


Vi er interessert i å vite subnet-adressene til disse to nye nettene:


Vi skaper en kontainer med BusyBox-komponenten og knytter den til de to nye nettverkene, i tillegg til bridge:


Nå starter vi kontaineren bb med BusyBox inni, og skriver kommandoen ip a for å se nettverkskonfigurasjonen på komponenten:

Fra denne kommandoen ser vi de tre ethernet-adaptrene eth0, eth1 og eth2 med subnettadresser som tilsvarer nettverkene bridge, my-net-1 og my-net-2. Som nevnt tidligere ville vi trolig koplet fra bridge for å unngå påvirkning fra uvedkommende komponenter.

Eksempel 2 – Trafikk mellom to Docker-komponenter

Det første eksemplet viste kun konfigurasjon av nettverksforbindelser, ikke hvordan vi kan bruke dem. Derfor vil vi nå sette opp komponenten first slik at vi kan kalle den fra busybox med linux-kommandoen wget og vise html-innholdet som first sender. Komponenten first er beskrevet i et tidligere blogginnlegg.


Her brukes kommandoene som er vist som metode nr.1 ovenfor og vi starter henholdsvis first og busybox med forbindelser til my-net-3. I busybox bruker vi kommandoene slik:

  1. ip a for å se at vi har fått et nettverksadapter med en adresse fra subnettet til my-net-3
  2. ping first for å vise at det foreligger en navnetjeneste som kopler navnet first (navnet på kontaineren) til ip-adressen 172.27.0.2, og at det foreligger en virkelig forbindelse dit.
  3. wget first:8080 for å sende et http-forespørsel til 172.27.0.2, port 8080 og lagre svaret på filen index.html.
  4. more index.html for å vise innholdet av denne filen på konsollet.

Vi har altså med dette eksemplet demonstrert at to komponenter kan ha en nettverksforbindelse som er skjermet fra andre komponenter. Det som er verd å huske er:

  • Interne nettverk får automatisk en subnett-adresse.
  • Containere som kopler seg til et internt nettverk får tildelt IP-adresse automatisk.
  • Interne nettverk (unntatt bridge) har en navnetjeneste som returnerer IP-adressen til navngitte containere. Derfor er det lurt å gi containere et navn med --name parameteren.

Av en eller annen grunn er det ingen navntjeneste i nettverket bridge, slik at ping first ikke vil fungere dersom komponentene ønsket å kommunisere over det nettverket.

Docker-compose

En applikasjon vil gjerne bestå av flere Docker-containere som samarbeider og må konfigureres og startes på en kontrollert måte. Enkeltkommandoer som vist i eksemplene ovenfor kan kanskje settes sammen i en kommandofil (.bat, .sh) og kjøres samlet. Dette forutsetter at du har konsoll-adgang til maskinen som skal kjøre applikasjonen, og at ulike maskiner kan forstå denne kommandofilen. Dette er forutsetninger som ikke kan garanteres. Docker-compose vil øke portabiliteten av applikasjoner og forenkle konfigurasjonen, siden alt foregår i én fil.

Derfor kommer Docker-compose inn som et verktøy hvor detaljer vedrørende konfigurasjon og kjøring for alle komponentene i applikasjon kan uttrykkes i én fil. Denne filen har en såkalt YAML-syntaks, som skal vises i eksemplet som følger.

Filen skal hete docker-compose.yml. Som eksempel på utforming skal vi bruke konfigurasjonen i eksempel nr.2 ovenfor. Innholdet i filen er som vist under:

Syntaksen og mulige informasjonselementer i denne YAML-filer (uttales “jammel”) er ganske omfattende, og en oversikt finner du her. Derfor velger jeg heller å demonstrere ett bestemt kjent tilfelle med en typisk struktur: nettverksforbindelse, volumer, eksponerte porter. Elementene under disse to vil være tilsvarende de parametrene vi tidligere ga på kommandolinjen. For first, legg merke til at build: nå må ha et stinavn til der hvor kildefilene til first ligger.

Syntaksen i den viste docker-compose.yml skal forstås som et hierarki der innrykk av teksten viser en “tilhørighet” til linjen ovenfor med kortere innrykk. Her har vi altså tre hovedkategorier: services, volumes, networks. Før en kontainer kan knyttes til et volum eller nettverk må disse først deklareres på denne måten. Under networks finner vi hvilket nett som skal brukes i applikasjonen og hva dette skal hete.

Under services finner vi de to vi har arbeidet med så langt, bb og firsts. For bb kommer det to linjer knyttet til stdin_open og tty, som har å gjøre med at vi ønsker konsolltilgang til denne kontaineren, noe som i mange tilfeller ikke vil være aktuelt. Derfor vil denne viste filen ha de fleste av de egenskapene du i praksis vil trenge.

La oss nå gi denne filen til Docker-compose og se hva som kommer ut av det. Arbeidskatalogen (current directory) må være den som inneholder docker-compose.yml . Oppstart skjer med kommandoen $ docker-compose up:


Nå er både bb og first started i hver sine kontainere, men vi får ingen konsolltilgang til å skrive kommandoer i. Til forskjell fra eksempel nr.2 må vi skrive dette i tilegg:
$ docker exec -it bb sh
Denne kommandoen utfører kommandoen sh i kontaineren som heter bb. Parameteren -it gjør at sh også kommuniserer med konsollet, så vi får en interaktivt Linux-konsoll. Nå kan vi teste at det er kommunikasjon mellom bb og first:


Dette må dog skrives i et annet CMD-vindu, fordi vinduet der vi startet docker-compose er opptatt. Der kan vi derimot lukke applikasjonen og stanse kontainerne ved å trykke ctrl-C noen ganger:


Kommandoen $ docker ps -a viser en liste over kontainerne og at disse er stoppet. Vi kan nå slette én og en kontainer med kommandoen $ docker rm bb first, men en enklere måte er $ docker container prune, som sletter alle inaktive kontainere under ett. Inaktive kontainere kan okkupere en del ressurser i systemet, og det er sunt å rydde regelmessig.

Docker swarms

I et storskala informasjonssystem vil en web-tjeneste kjøre på mange maskiner i parallell. Da oppnår man at maskinene kan fordele kundetrafikken mellom seg for å oppnå høyere total ytelse (kalt lastfordeling), og at noen av maskinene kan falle ut av drift uten at systemet som sådan blir utilgjengelig (kalt fail-over). Denne formen for ressursorganisering krever en sjef (manager), som fordeler forespørsler mellom arbeiderne (workers) og som holder oversikt ettersom maskiner går inn eller ut av drift.

Docker-arkitekturen egner seg godt til å inngå i en slik storskala arkitektur, siden tilstandsløse komponenter kan dupliseres på flere maskiner og utføre nøyaktig den samme tjenesten. Kunder vil se ett eneste servicepunkt (IP-adresse og port) og ikke merke noe til at tjenesten er fordelt på mange maskiner. La oss derfor se på hvordan Docker-komponenter kan organiseres som en Docker swarm:

Bruk helst Linux-maskiner

Selv om kommandoene for Docker-svermer også finnes på Windows-versjonen av Docker-systemet, er det en del egenskaper som simpelthen ikke virker som forventet. Det er derfor å anbefale at et eksperiment med Docker-svermer kun benytter Linux-maskiner. Husk også at Docker-komponenter ikke er arkitekturnøytrale, de kan ikke kjøre både på X86- og ARM-maskintyper. Ikke blande f.eks. Raspberry Pi (ARM) med ordinære PCer (X86) i en Docker-sverm.

Alt foregår fra sjefen

For å bygge opp en sverm må maskinene kunne nå hverandre gjennom et IP-nettverk, det er ikke tilstrekkelig at sjefen kan nå alle arbeiderne, arbeiderne må også kunne kommunisere med hverandre. For først å bygge opp en sverm gjør vi følgende:

  1. Bestem hvilke maskiner som skal delta, skriv ned deres IP-adresser og lag et kart som viser hvem som er sjef og hvem som er arbeidere. Fra konsollet til sjefen, logg inn (med ssh) på alle arbeiderne i separate konsollvinduer. Alle kommandoer som vises som eksempler må kjøres i sudo-modus (skriv sudo bash for komme dit).
  2. På alle maskinene i svermen, sørg nå for at alle kontainere er stanset ($ docker stop ..., $ docker rm ...) og kontroller resultatet med $ docker ps -a.
  3. På sjefens maskin, sjekk om det allerede er en sverm med $ docker node ls. Dersom det finnes en, fjerne dem med kommandoen $ docker swarm leave --force.
  4. På sjefens maskin, opprett en sverm med kommandoen $ docker swarm init. Reponsen kan se omtrent slik ut:
    Swarm initialized: current node (icv8b9vnemxfmym4lb1gbto7f) is now a manager
    To add a worker to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-0ezzbg9iw2glu6d7xd6c2shjvhadr7zlemwuf4l9besxl2iogl-a1yngnqehgyub0phopuslysly 192.168.2.116:2377
  5. Ta en kopi av den uthevede teksten, og lim den inn i konsollvinduet til alle arbeiderne slik at kommandoen utføres der. De vil nå kople seg til sjefen (til ip-adressen som er vist i kommandoen) og slutte seg til svermen. Nå vil de etterhvert få opprettet kontainere for applikasjonstjenester og motta klientforespørsler.
  6. Fra sjefens konsoll skriv $docker node ls for å kontrollere at alle arbeiderne er kommet inn i svermen.

Utplassering av en tjeneste i svermen

Nå kan sjefen plassere en tjenesten inn i svermen. Dette betyr, som tidligere nevnt, at en applikasjon blir plassert ut på én eller flere av arbeiderne (inkludert sjefen), og en forespørsel fra en klient blir betjent av én av dem.

Hvilken IP-adresse leder inn til den utplassert tjenesten? Svaret er at alle IP-adressene i svermen (altså endepunktet til alle arbeiderne og sjefen) gir adgang til tjenesten, også de arbeiderne som ikke betjener selve tjenesten. Det eksisterer et eget “overlay” nettverk mellom maskinene som fremforhandler fordelingen av arbeidsoppgaver og som videresender forespørsler fra klienter til en kandidat-arbeider.

Kommandoen skal skrives på sjefens konsoll. Sånn kan den se ut:

$ docker service create --name first --replicas 3 --publish 8080:8080 andfon7a/first

Den eneste nye parameteren siden de tidligere eksperimentene er --replicas som angir det maksimale antall arbeidere (inkl. sjefen) som skal laste denne applikasjonen. Legg også merke til at Docker-komponenten ikke må ligge på sjefens egen maskin, den kan hentes fra Docker-hub om nødvendig (og den må da evt. plasseres der på forhånd med $ docker push andfon7a/first).

Med kommandoen $ netstat -ant på alle maskinene i svermen kan man nå konstatere at port 8080 er “åpen”. Med en web-leser kan man kontake én av maskinenes IP-adresse på port 8080 og konstatere at tjenesten utføres korrekt.

For å inspisere tjenesten bruk kommandoen $ docker service inspect --pretty first. For å fjerne den skrives $ docker service rm first.

Opp- og nedskalering: Det opplagt nyttige i svermen er muligheten for å endre kapasiteten ved å øke eller redusere antall maskiner som deltar i en tjeneste. Det gjøres som følger (i dette eksemplet reduseres antallet fra 3 til 2):

$ docker service scale first=2

Konklusjon

Målet med dette blogginnlegget var å gi en kortfattet, men ukomplett, innføring i hvordan man setter opp nettverk og svermer av Docker-komponenter. Det fulle omfanget av muligheter og detaljer er mye større, men er blitt utelatt fordi det er lagt vekt på å vise konkret hvor enkelt det er å sette opp en grunnleggende tjeneste.

Jeg anbefaler først leseren å gjennomføre disse eksemplene på eget utstyr og så gjøre nødvendige endringer etter egen interesse: F.eks. hvordan en sverm kan operere med permanent lagring gjennom en filtjener eller en SQL-database. Slike komponenter er ikke enkle å replikere i en sverm og vil naturlig ligge i egne tjenester, gjerne i frittstående Docker-kontainere med muligheter for å lagre data i volumes.

Utplassering av Docker-komponenter i Microsoft Azure

 39 total views,  1 views today

Anders Fongen, september 2022

Å konstruere Docker-komponenter og utplassere (eng. deploy) dem i en lokal kontainer på egen maskin er en ganske overkommelig oppgave. Mer omstendelig er det å sette slike komponenter i drift i en sky. De store skytjenestene (f.eks. Amazon AWS, Google Cloud og Microsoft Azure) er svært store og komplekse installasjoner som skal kunne betjene kunder med helt ulike behov. De tilbyr alle en web-tjeneste (portal) som lar kunden laste opp egenutviklet programvare og konfigurere samspillet med de tjenestene som skyen tilbyr (f.eks. sikkerhet, backup, navnekataloger, fillagring og SQL-databaser). For alle disse skyene blir denne portalen svært omfattende og omstendelig å bruke. Kvaliteten på dokumentasjonen er dessuten svært varierende.

Jeg har tatt for meg utvikling, utplassering, feilsøking og drift av Docker-komponenter med påfølgende utplassering i Microsoft Azure. Etter vellykket utplassering er tjenesten tilgjengelig på Internet. Dette blogginnlegget vil vise hvordan det kan skje.

Hvorfor er skyen en god idé?

Visst er det mulig å sette opp en webtjener på ditt eget hjemmenett, bestille fast IP-adresse hos Internet-leverandøren, sette opp port forwarding på hjemmeruteren, og gi hjemmeadressen et DNS-navn. Denne prosessen har jeg forøvrig beskrevet i et eget blogginnlegg.

Med et slik løsning må du selv ta hånd om sikkerhet, backup, reserveløsninger ved systembrudd, brukertillatelser, og fremfor alt skalering. En vellykket tjeneste på Internet kan oppnå en svært rask vekst i kundemengden, og dersom du ikke klarer å opprettholde en akseptabel responstid under slike forhold har du mislykkes.

Disse kravene kan skyen innfri, fordi de besitter store datasentraler med høy nettverkskapasitet, og de kan raskt øke kapasiteten på tjenesten din, de kan endog gjøre det automatisk. Personellet i datasentralene er eksperter på skalering, sikkerhet, overvåking og etterforskning, og de har ingen annen jobb enn å passe på at tjenestene leverer stabilt og raskt. Du har andre oppgaver og vil ikke klare å gjøre denne jobben like godt.

Docker

Programvare som skal kjøres som en skytjeneste må skrives etter bestemte regler, og utplasseringen trenger noen opplysninger som må fremskaffes av kunden. Docker er programvare for å lage og kjøre skytjenester. Jeg skal ikke forklare Docker grundig her, fordi det finnes mye informasjon om nettopp dette på nettet:

Det er nødvendig å gå gjennom noen eksempler på bygging og kjøring av Docker-komponenter på lokal maskin: Når Docker-komponenter kjøres på lokal maskin, f.eks. BusyBox, kan de kommunisere med brukeren gjennom konsollet, dvs. skjermen som brukes for å bygge og starte komponenter. Dette er ikke mulig på Azure, hvor komponenten må kommunisere med omverdenen gjennom nettverket (Internet).

Nettverkstrafikk kan omfatte protokoller som ssh, http, mqtt og det meste annet, men komponenten må selv besørge selve protokollen, Azure besørger bare UDP- og TCP porter, samt en brannvegg for å beskytte mot angrep via nettverket. Vi skal demonstrere bruk av Azure med web-komponenter som benytter http-protokoll.

Installasjon av programvare

For å kommunisere med Azure trengs programvaren Docker Desktop, installert på Windows eller Mac (bruk lenken ovenfor). I CMD-konsollet (Windows) kan man nå skrive $ docker for å utføre diverse kommandoer.

I den påfølgende tekst vil jeg bruke “$”-symbolet for å vise en kommando slik den skal skrives inn i CMD-konsollet. Dollartegnet skal altså ikke skrives inn.

For å utplassere Docker-komponenter på Azure trenger du følgende brukerkontoer:

  1. Hos hub.docker.com. Her plasserer du dine komponenter slik at andre kan laste dem ned til seg og bruke dem. Kontoen er gratis.
  2. Hos portal.azure.com. Her foregår selve kontrollen av Azur-aktivitetene. Duu må registrere et betalingskort, men du får også en romslig kvote gratis som du kan eksperimentere med.

Eksperimentprogrammer

To små Python-programmer blir brukt i denne demonstrasjonen: De ene er et enkelt tilstandsløst web-program som adderer to tall og viser resultatet, det andre web-programmet benytter en tekstfil som lagres på et permanent lager. Skillet mellom disse to programmene er hvordan de bruker permanent lager. Programmene ser slik ut:

Summere to tall (tilstandsløst web-program)

first.py:
import web
urls = ('/','index')

class index:
   def GET(self):
      f = open("first.html","r")
      return f.read()
   def POST(self):
      i = web.input()
      if i.a.isnumeric() and i.b.isnumeric():
         return "Svaret er %d" % (int(i.a)+int(i.b))
      else:
         return "Kun numerisk inndata er lovlig"

if __name__ == '__main__':
   app = web.application(urls,globals())
   app.run()

first.html:
<html><body>
  <h1>Addere to heltall</h1>
  <form method='POST'>
     <input name='a', size='4'>
     <input name='b', size='4'>
     <input type='submit' value='finn sum'>
   </form>
</body></html>

Dockerfile:
FROM python:3.8
WORKDIR /usr/src/app
COPY . .
RUN pip3 install web.py
EXPOSE 8080
CMD ["python","./first.py"]

Dette webprogrammet kan startes direkte fra kommandolinjen og tilby en enkel tilstandsløs tjeneste. Vi kan når som helst restarte tjenesten uten å forstyrre klientene. Vi kan lage en Docker-komponent og kjøre den lokalt på vår egen maskin med disse kommandoene (de tre filene må ligge på samme katalog, og katalogen må være arbeidskatalog (current directory):

$ docker build -t first .
$ docker run --name container1 -d -p 8080:8080 first

Nå er denne webtjenesten tilgjengelig på http://localhost:8080/. For å stoppe og fjerne tjenesten (f.eks. før utplassering av en ny programversjon) kan disse kommandoene skrives:

$ docker ps -a
$ docker stop container1
$ docker rm container1

Dele en liten datafil (tilstandsfylt web-program)

third.py:
import web, os

urls = ('/','index')
os.environ['PORT'] = '80'
app = web.application(urls, locals())
filename = "message.txt"
class index:
   def GET(self):
      if not os.path.isfile(filename):
         f = open(filename,"w")
         f.close()
         message = "Ingen melding"
      else:
         f = open(filename,"r")
         message = f.read()
      resp ="<html><body><h2>Din siste melding var: %s"%(message)
      resp += "<p>Skriv inn melding:<form method='POST'><input name='m' size='20'>"
      resp += "<input type='submit' value='Lagre melding'></form></body></html>"
      return resp
   def POST(self):
      i = web.input()
      f = open(filename,"w")
      f.write(i.m)
      f.close()
      raise web.seeother('/')

if __name__ == '__main__':
   app.run()

Dockerfile:
FROM python:3.8
WORKDIR /usr/src/app
COPY . .
RUN pip3 install web.py
EXPOSE 80
CMD ["python","./third.py"]

Dette web-programmet vil ta imot en tekstlinje og lagre den på en fil, som siden kan hentes frem av denne eller andre klienter. En form for datadeling altså. Filen som lagrer tekstlinjen bør bevares også når programmet stopper og starter igjen. Dersom vi kjører third.py som et vanlig Python-program vil filen ligge på vertsmaskinens filsystem, og vil dermed bevares slik vi ønsker.

Dersom vi lager en Docker-komponent som med forrige eksempel, vil også denne tekstlinjen bevares og utveksles mens Docker-komponenten kjører. Dersom Docker-kontaineren stoppes og startes igjen:

$ docker stop container1
$ docker start container1

(gitt at kontaineren er gitt navnet container1 ved docker run – kommandoen), da vil fortsatt tekstlinjen bevares slik vi ønsker. Dersom vi derimot fjerner kontaineren og lager en ny:

$ docker stop container1
$ docker rm container1
$ docker run --name container1 -d -p 80:80 third

Da har tekstlinjen fått det innholdet den hadde da Docker-komponenten ble laget (evt. tom), og endringer etter det tidspunktet er gått tapt. Altså, ikke slik vi ønsker at webtjenesten vår skal fungere.

Docker-komponenter med volumes

For å kunne bevare data også når docker-kontaineren slettes og skapes på nytt, må dataene lagres utenfor kontaineren, i vertsmaskinens filsystem. Dette er mulig å få til gjennom å montere en del av vertsmaskinens filsystem inne i kontainerens. Vi skal først endre én programsetning i third.py:

filename = "/app/message.txt"

og bygger web-tjenesten på lignende måte:

$ docker build -t third .
$ docker run --name container3 -v textdata:/app -d -p 80:80 third

Da oppnår vi nemlig det vi ønsker, nemlig at innholdet på området textdata i vertskapets filområde (i et nærmere bestemt filområde inne i Docker-installasjonen) vises under katalogen /app inne i kontaineren, og data som lagres under /app blir bevart selv når kontaineren slettes og gjenskapes. Dette kan kontrolleres ganske enkelt i et eksperiment.

Merk at dette permanente lageret kan lagre alle slags objekter, ikke bare tekstfiler: Bilder, lyd og selvfølgelig en databasefil fra f.eks. SQLite.

Litt om tilstander i Azure/Docker

Azure, i likhet med andre skyleverandører, lar Docker-komponenter kjøre i omgivelser som støtter stor skala, høy sikkerhet og dynamisk konfigurasjon. Det vil si at web-tjenesten som kjører kan gis et elastisk ressurstilbud ettersom pågangen øker og minker. Slik automatisk skalering skjer gjennom at Docker-komponenter kjører samtidig på mange maskiner i parallell (kalt flere instanser) og fordeler forespørselene mellom seg, og at antallet samtidige maskiner øker og minsker dynamisk.

Med datalagring inne i komponenten, slik vi har demonstrert med third.py, vil ikke en slik skalering kunne finne sted, fordi alle instansene vil ha ulikt datainnhold siden de betjener ulike forespørsler. Nei, skalering krever at Docker-komponenten benytter en separat lagringstjeneste som behandler skrivbare data (som filen message.txt i third.py ovenfor). Kun lesbare data kan fortsatt legges inne i Docker-komponenten dersom mengden ikke er for stor.

Dette skillet mellom brukerbetjening, forretningslogikk og datalager er en populær og velprøvd konstruksjonsteknikk for store informasjonssystemer. Trafikken mot datalageret (kalt back-end) er ofte lavere enn mot front-end (web-tjenesten) og datalageret kan skaleres med andre teknikker enn en front-end. Derfor ser vi at dette skillet mellom utføring og lagring i adskilte tjenester går igjen hos alle skyleverandører.

Utplassering av third.py i Azure

Vi skal hoppe over utplassering av first.py i Azure fordi det skjer på samme måten som med lokal Docker-plattform med noen små endringer, som vil fremgå av forklaringen som følger. Jeg skal gå gjennom utplassering av third.py i Azure, etter at den ene programsetningen er endret (filename = "/app/message.txt). Trinn for trinn er prosessen denne:

  1. Bygge en lokal Docker-komponent. Navnet på komponenten må prefikses med brukernavnet i Docker-hub, som i mitt tilfelle er andfon7a:
    $ docker build -t andfon7a/third .
  2. Plassere komponenten i Docker-hub. Det er nemlig herfra at Azure skal hente den senere
    $ docker login (Her kreves brukernavn og passord til brukerkontoen din)
    $ docker push andfon7a/third
    $ docker logout
  3. Logge inn i Azure og opprette filområdet
    $ docker login azure (Får du feilmelding “Azure lookup failure”, har du feil
    versjon av Docker installert. Bruk nyeste versjon av Docker desktop)
    $ docker context create aci mycontext (velg “create new resource group”)
    $ docker context use mycontext
    $ docker volume create mydata --storage-account andfon7astorage
  4. Lag en kontainer og start komponenten i den. Den lastes inn fra Docker-hub
    $ docker run -p 80:80 --name third --domainname andfon7a -d -v andfon7astorage/mydata:/app andfon7a/third
  5. Sjekk at kontaineren kjører og hvilket DNS-navn den har fått.
    $ docker ps -a
    – Sjekk nå med en web-leser at du får tak i tjenesten med dette DNS-navnet

Microsofts egen infoside om denne prosessen finner du her. Docker sin tilsvarende veiledning finnes her.

For å stoppe/starte/fjerne kontaineren bruker du kommandoene:
$ docker stop third
$ docker start third
$ docker rm third

Starting av en kontainer kan ta lengre tid enn kun kommandoen i konsollet. Bruk $docker ps -a for å se status i oppstartsarbeidet.

Om DNS-registreringen: For bruk av --domainname parameteren gjelder det at DNS-navnet blir registrert på den tildelte IP-adressen med en TTL-verdi på 300 sekunder. Dersom du under f.eks. eksperimentarbeid statid starter containeren på nytt kan du komme i situasjonen hvor DNS-tjenesten fortsatt en stund returnerer den forrige IP-adressen. Om du utelater --domainname parameteren vil $docker ps vise deg IP-adressen i den samme kolonnen , som alltid er riktig.

Huskn

Når du vil igjen jobbe lokalt eller mot Docker-hub:
$ docker logout azure
$ docker context use default

Docker-tjenester fra Amazon AWS og Google Cloud

Amazon AWS og Google Cloud er to konkurrerende skytjenester som i det store bildet har mye til felles. Begge disse kan enkelt støtte tilstandsløse Docker-komponenter, enklest gjennom egne kontrollprogrammer (à la docker) som installeres i Windows-konsollet (cmd).

  • Google Cloud betjenes av programmet Gcloud som installeres på din lokale maskin. Opplastingen skjer direkte fra lokal maskin med commandoen gcloud run deploy, og nødvendige tilleggsopplysninger legges inn i den påfølgende dialogen.
  • For Amazon AWS skjer utplasseringen ved hjelp av en tilleggsfil som inneholder de nødvendige ekstraopplysningene (som ikke ligger i Dockerfile). For en veiledning for dette, sjekk avsnittet “Docker on AWS” på denne websiden.

Ingen av disse to skytjenestene støtter derimot Volumer, slik vi har vist er mulig i Azure. Derimot har begge database-tjenester som kan brukes av Docker-komponenter for lagring. Dette dekker noe av behovet for permanent lagring, men krever også at programvaren i større grad er tilpasset for dette og blir derfor mindre portabel enn hva som er ønskelig. Databasetjenester er dessuten krevende å konfigurere, sammenlignet med et filsystem.

RSA-algoritmen forklart

 18 total views,  1 views today

Om du kjenner prinsippene for public key kryptografi, har du sikkert hørt om RSA-algoritmen, som er den viktigste metoden for å kryptere med offentlig nøkkel. I dette videoforedraget gir jeg en forklaring på hvordan RSA-algoritmen virker, og gir et bevis på det matematiske grunnlaget for metoden. Jeg viser også hvordan nøkkelen for dekryptering kan utledes, og gir en begrunnelse for hvorfor RSA-algoritmen er vanskelig å knekke. God fornøyelse.

Adding SIP phones to an Allstarlink node

 75 total views,  2 views today

Introduction

Since the Allstarlink node is based on telephone switch (PBX) software (called Asterisk), it is possible to include a landline or VoIP telephone in the Allstarink community, allowing it to communicate and command the Allstarlink node with DTMF commands.

This short document gives step-by-step instructions in how to obtain this connection. You must be able to log into your Allstarlink node (with SSL or local console) and edit the configuration files in order to follow them.

Step 1: Preparing Asterisk for the SIP protocol

VoIP phones (and phone adapters) will use the SIP (Session Initiation Protocol) for setting up and taking down phone connections. By default, the Allstarlink’s configuration does not allow this, so it must be enabled in its configuration as follows:

Make the following modifications to /etc/asterisk/modules.conf:
Change the line: unload => chan_sip.so to load => chan_sip.so

Make the following modifications to /etc/asterisk/sip.conf:
In the section [general], change the line
bindaddr = 127.0.0.1 into bindaddr = 0.0.0.0 

Test procedure: Restart Asterisk with # service asterisk restart (the # sign indicates that this must be done in super user mode, write sudo bash to get there). Now the command $ netstat -anu should return a list with the following line included:

udp        0      0 0.0.0.0:5060            0.0.0.0:*

If not, fix the problem before moving to the next step.

Step 2: Register phone extension

The file /etc/asterisk/sip.conf must contain a list of extension numbers and related information. It should contain a number of sections like this:

[211]
type=friend
host=dynamic
username=211
secret=1234
dtmfmode=rfc2833
context=sip-phones
callerid="Anders-PC" <211>

In this example, the extension numbers are in the range 200-299. Make at least two copies of this section and change the numbers accordingly. The “secret” is a password used by the VoIP phone. If your Allstarlink node is reachable from the Internet, the password should be strong, not “1234”.

Test procedure: Restart Asterisk with # service asterisk restart. Install a SIP Phone app (often called softphone) on at least two devices in your network. I am using “MicroSIP” on Windows and “Mizudroid” on Android. Configure them according to the Allstarlink’s IP address, extension number and password. The following illustrations serves as an example on the MicroSIP configuration screen:

The MicroSIP main screen should now indicate “online” on the bottom line.

Configure two softphones on two different devices with different extension numbers, and make sure they are indicating a successful connection with the Allstarlink node. Do not move on to the next step until this is verified.

Step 3: Set up extension handling

The next step involves editing of /etc/asterisk/extensions.conf. First, make a copy of the original file in case you want to return to the original configuration later:

# cp extensions.conf extensions.conf.orig

Above the last line in the file (#includeifexist….) include this section in extensions.conf

[sip-phones]
exten => _2XX,1,Dial(SIP\/${EXTEN},60,rT)
exten => 300,1,Ringing()
exten => 300,n,Wait(2)
exten => 300,n,Answer()
exten => 300,n,rpt(51201|P); Change 51201 to your own node mumber
exten => 300,n,Hangup

The first exten-line allows the softphones to connect to each other, so we try this first. The lines with “exten => 300…” allows the phones to connect to the repeater. Remember to enter your own node number in the place of the “51201”

Test procedure: Restart Asterisk with # service asterisk restart. From one softphone, dial the number of the other and check that there is a voice connection between them. Connect also in the opposite direction. Do not move on until this is successful (there are often some problems in this step which needs to be sorted out.

Now you may try to dial “300” to connect to the repeater. You should hear one “ring” in the softphone and then silence. You are hopefully now connected to the repeater, and can command it with DTMF, e.g. *712 to have it say the time of day. You can connect the repeater to another node with *3xxxxxx and start talking. Your PTT is pressed with the *99 code and released with #.

Enjoy your new function and move on to the next step if you want to allow the repeater to connect to the softphones from a radio handset.

Step 4: Connecting to VoIP phones from a local radio handset

This step will enable a handset local to the Allstarlink node to set up a voice connection to a SIP phone using DTMF codes. To dial from a handset local to a different Allstarlink node does not require more configuration and is described in Step 5.

The file /etc/asterisk/rpt.conf should be modified as follows: Uncomment these lines around line no. 232 and give them this content:

61=autopatchup,noct=0,quiet=1,farenddisconnect=1,dialtime=5000
62=autopatchdn

These lines will allow the radio side to initiate a phone connection through the use of DTMF codes *61xxx and disconnect with *62 (unless the phone hangs up first). The call will be given the default context value “radio” and processed by the rules in extensions.conf.
There are several ways to modify this file to obtain the desired effect, one of which is shown here:

Modify the file /etc/asterisk/extensions.conf as follows: Make sure that the [radio] section has the following content:

[radio]
exten => _2XX,1,Dial(SIP/\${EXTEN})

The other lines in the same section should be commented out. The pattern _2XX matches the number range 200-299 and may be changed according to your own number plan.

This is the simplest possible modification to extensions.conf, but it breaks the neat logic in the distributed file. Please study this logic structure if you want a more “proper” modification.

You may not succeed in disconnecting with *62 if your repeater uses simplex communication (same frequency for transmit and receive) while the squelch indicator is lit. If noone picks up the phone, the ringing tone you hear in the handset will stop you from disconnecting (because the repeater doesn’t listen while it is transmitting), and you will have to wait until the ringing times out.

Test procedure: Restart Asterisk with # service asterisk restart. From a radio handset, dial *61210 (given that your SIP phone has extension 210). The phone should now ring, and when you answer the call you should be communicating with the handset without the use of DTMF codes *99 and #. You will still need to press PTT on the handset prior to talking. The phone is given a VOX function, which means that the handset should not press PTT while the squelch indicator is lit.

Step 5: Connecting to VoIP phones on a different node

It is now even possible to connect to a VoIP phone from a handset connected to a different Allstarlink node. With the radio handset, follow these steps:

  1. Connect to the other node (where the VoIP phone is connected) with DTMF *3xxxxx
  2. Set the other node in “command mode” with *4xxxxx (same node numer as in line 1)
  3. Call the VoIP phone with *61210 (given the extension 210)

Test procedure: Follow the steps above and observe the result. The comments from the test procedure in Step 4 applies as well.

Finally

I hope this little text may provide fun and a little experience in the configuration of Asterisk. Another fun addition is to connect a landline phone through a VoIP adapter, e.g. the Linksys SPA2102, which I bought from Ebay for $15. Get the nostalgic feeling of talking to a repeater net through a landline phone!

Finally, do not hesitate to send suggestions for improvements and clarifications.

Bygg din egen hjemmeruter med Raspberry Pi

 19 total views,  1 views today

En Raspberry Pi kan erstatte din hjemmeruter, eller komplettere hjemmeruteren med smarte og fleksible tilleggstjenester. Les mer om det her.

Innledning

En ruter er en datamaskin, og en datamaskin er en ruter.

Har du en Raspberry Pi eller en gammel datamaskin som du kan avse til dette formålet, kan du utvikle din egen hjemmeruter med flere funksjoner enn en “standard hjemmeruter”, og med større fleksibilitet for å møte dine behov. Her er noen eksempler:

  • Det er behov for å ha tjenermaskiner inne i ditt lokalnett, og du ønsker å anrope dem med internet-navn, ikke med IP-adresser.
  • Det er ønske om å bruke IP-protokoll versjon 6 (IPv6)
  • Du trenger å bruke Virtuell Private Nett, men ønsker ikke å kople dette opp og ned til stadighet. La ruteren gjøre dette og tilby adgangen til VPN for hele lokalnettet ditt.
  • Det er behov for å bruke en Dynamisk DNS-tjeneste, men ruteren støtter ikke den ene du ønsker å benytte deg av.
  • Du ønsker å kombinere ruteren med en web-tjener, en mediatjener, filtjener eller andre tjenester som hele lokalnettet skal kunne ha glede av.
  • Det er ønske om adgang til lokalnettet fra utsiden av ruteren (Internet), men også ønske om en sterkere beskyttelse mot angrep og hacking.
  • Ruteren skal brukes til “smart home” oppgaver.
  • Ruteren skal være en telefonsentral for Internet-telefoni

Denne artikkelen skal beskrive tre forskjellige måter å benytte en Raspberry Pi på, enten som fullstendig erstatning for hjemmeruteren, eller som et supplement hvor større eller mindre deler av tjenestene i ruteren beholdes slik de er, men suppleres av Raspberry Pi. Disse tre alternativene er vist som alternativ a-c på Figur 1.

Figur 1: Tre måter å bruke Raspberry Pi på

I alternativ (a) har Raspberry Pi helt erstattet hjemmeruteren, og tar hånd om kontakten med Internet-leverandøren og spredningen i lokalnettet med Ethernet og WiFi. Alternativ (b) lar Raspberry Pi besørge kontakten med Internet, mens hjemmeruteren er beholdt for spredning til lokalnettet. I alternativ (c) har hjemmeruteren som tidligere kontakt med Internet og ansvaret for spredning i lokalnettet, mens Raspberry Pi utfører et sett med ektraoppgaver for å gjøre nettet med funksjonelt og fleksibelt.

Figur 2 viser et funksjonsdiagram for en hjemmeruter. Ruteren tilbyr tjenester knyttet til DNS, DHCP, NAT, brannvegg, ruting, vitsjing, og muligens VPN. Alt dette skal erstattes av en Raspberry Pi om alternativ 1 realiseres. Alternativ 2 er vist som den grønne firkanten nede til høyre, hvor ruterens spredefunksjoner for Ethernet og WiFi er beholdt, og ikke besørget at Raspberry Pi.

Figur 2: Funksjonelt diagram av en hjemmeruter

De neste avsnittene vil drøfte fordeler og ulemper med de ulike alternativene, og gi en del veiledning i hvordan de kan realiseres.

a. Raspberry Pi som selvstendig hjemmeruter

En Raspberry Pi (heretter kalt RPi) har et WiFi-adapter og et Ethernet-adapter innebygget, samt 4 USB-porter. Ethernet-adapteret (kalt eth0) koples til Internet-kontakten. WiFi-adapteret settes opp som et aksesspunkt (AP) som maskiner på lokalnettet kan kople seg til.

Om man i tillegg ønsker Ethernet-kabling i lokalnettet løses dette ved å anskaffe Ethernet-adaptere med USB-plugg. Man kan plugge inn flere slike, eller kople den til en Ethernet-svitsj. Denne artikkelen vil videre vise hvordan et slikt USB-Ethernet-adapter skal konfigureres. Du trenger egentlig ikke kjøpe en egen Ethernet-svitsj, fordi din gamle hjemmeruter kan brukes til dette.

Linux tillater ikke lenger at maskiner tilkoplet henholdsvis WiFi og Ethernet befinner seg i samme IP subnett. Derfor blir det laget to separat subnett for disse. I eksemplene brukes IP-adressene 192.168.3.0/24 og 192.168.4.0/24 for disse to.

Grunnkonfigurasjonen av en RPi blir ikke forklart her. Du kan gjerne laste ned en “lite”-versjon av operativsystemet for å spare plass, fordi du ikke får bruk for et grafisk betjeningssystem allikevel. Likeså blir ikke grunnleggende betjening av RPi forklart, så finn ut hvordan man logger seg inn, restarter osv.

Kommandoer som innledes med #-tegnet krever administrator (sudo) tillatelser. Skriv sudo bash for å oppnå dette. Kommandoer som innledes med $-tegnet krever ikke dette.

Programvare som må installeres

Du installerer en rekke ekstra programmer i RPi for å komme i gang. Dette er programmer som kreves for de ulike oppgavene.

# apt-get update
# apt-get install hostapd openvpn dnsmasq dnsutils lighttpd

For å installere en såkalt pubsub-broker: # apt-get install mosquitto
For å installere en telefonsentral: # apt-get install asterisk
For å installere filtjener: # apt-get install samba
For å installere Dynamic DNS-klient: # apt-get install ddclient

(Under installasjonen av ddclient kreves det noen svar på konfigurasjonsspørsmål, men man kan skrive inn hva som helst her. Siden skal riktige konfigurasjonsfiler skrives).

Sett maskinen til å rute ipv4:

# nano /etc/sysctl.conf

Sett inn linjen net.ipv4.ip_forward=1 (fjern evt. kommentar tegn foran linjen som allerede finnes der). Reboot er nødvendig, men det kan vente til senere.

Fjerne også kommentargnet på linjen net.ipv6.conf.all.forwarding=1 om det er ønske om å tillate bruk av IPv6.

Tildele adresser til adaptrene

Skriv følgende innhold i filen /etc/network/interfaces:

# nano /etc/network/interfaces
auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet static
    address 192.168.3.1/24
auto wlan0
iface wlan0 inet static
    address 192.168.4.1/24

Sett opp WiFi aksesspunkt

Programmet hostapd setter opp WiFi-adapteret som et Aksesspunkt, som andre maskiner kan kople seg til.

# nano /etc/default/hostapd

Endre linjen DAEMON _CONF= til DAEMON_CONF=”/etc/hostapd/hostapd.conf” (husk anførselstegnene)

# nano /etc/hostapd/hostapd.conf

Sett inn dette innholdet:

interface=wlan0
ssid=MinHjemmeruter
country_code=NO
hw_mode=g
channel=7
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=MittPassord
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
wpa_group_rekey=86400
ieee80211n=1
wme_enabled=1

(teksten i rødt kan du erstatte med dine egne verdier)

# systemctl unmask hostapd
# systemctl enable hostapd

NB! Hostapd-tjenesten har erfaringsmessig problemer med enkelte WiFi-kretser, inkludert noen som står på RPi-kretskortet. Om det ikke lykkes å få dette til å virke, vurder heller alternativ (b), som overlater WiFi-kontrollen til den eksisterende hjemmeruteren.

Sette opp DHCP- og DNS-tjeneste

Programmet dnsmasq vil sørge for disse tjenestene, og programmet konfigureres ved å redigere filen /etc/dnsmasq/dnsmasq.conf. På eksemplet nedenfor vises at det tilbys IP-adresser til klienter som kopler seg til eth1 og wlan0, siden IP-adressene på disse adaptrene omfattes av de to dhcp-range setningene. Server-ordet brukes for å si hvor dnsmasq skal videresende DNS-spørringer som den ikke kan svare på med opplysningene i /etc/hosts.

# nano /etc/dnsmasq/dnsmasq.conf
server=8.8.8.8
dhcp-range=eth1,192.168.3.100,192.168.3.200,255.255.255.0,24h
dhcp-range=wlan0,192.168.4.100,192.168.4.200,255.255.255.0,24h

Denne konfigurasjonen medfører at klienter som kopler seg til Ethernet får tildelt en adresse i området 192.168.3.100-200, mens WiFi-klienter får tildelt adresser i området 192.168.4.100-200. Alle får tildelt adressen til RPi som default gateway og DNS-tjener. DNS-tjeneren vil hente data fra 8.8.8.8 (Googles DNS-tjener) dersom den ikke finner navneopplysninger i /etc/hosts eller i egen DNS-tjener.

Dnsmasq har mange konfigurasjonsmuligheter for å f.eks. reservere bestemte adresser til tjenermaskiner osv. Dette beskrives i dnsmasq.conf slik:

dhcp-host=11:22:33:44:55:66,192.168.3.25

Merk at i dette tilfellet vil maskinen med denne MAC-adressen kun få den reserverte IP-adressen dersom den er koplet via Ethernet, ikke via WiFi. Ellers vil den få en dynamisk adresse.

Hver gang det er gjort endringer i /etc/dnsmassq.conf eller /etc/hosts må den laste inn konfigurasjonsfilen på nytt. Det gjøres med denne kommandoen:

# service dnsmasq restart

Konfigerere brannvegg og NAT-funksjon

Programmet iptables styrer noen kompliserte filter- og behandlingsfunksjoner i nettverksprogramvaren. Her følger de reglene som gir en fornuftig brannvegg og NAT (Network Address Translation, klikk her for en egen beskrivelse).

Iptables kan konfigureres på flere måter, men vi nøyer oss i denne omgang med å bruke kommandoer som skrives inn i et konsollvindu, eller lagres i et skall-skript. Et eksempel på en fornuftig konfigurasjon av iptables er vist under, under den forutsetning at adaptrene eth1 og wlan0 er koplet til lokalnettet som skal beskyttes mot angrep fra Internet via eth0. Denne listen av kommandoer legges inn i filen /etc/rc.local slik at kommandoene utføres når maskinen starter opp.

iptables -F
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i eth1 -j ACCEPT
iptables -A INPUT -i wlan0 -j ACCEPT
iptables -A INPUT -i eth0 -p udp --dport 1194 -j ACCEPT
iptables -A INPUT -i eth0 -m conntrack --ctstate ESTABLISHED,RELATED 
		-j ACCEPT (på samme linje som over)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE


Port forwarding:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 
                 -j DNAT --to 10.10.3.10:8080 (på samme linje som over)
iptables -A FORWARD -p tcp -d 10.10.3.10 --dport 8080 -j ACCEPT

Linjen merket i rødt er eksempel på regler som slipper inn helt spesifikk trafikk. I dette tilfellet slipper UDP-segmenter til port 1194 inn til RPi, som er endepunktet for OpenVPN-tjenesten.

I andre sammenhenger ønsker man å gjøre tilgjengelig tjenester på maskiner inne i lokalnettet, noe som løses med såkalt port forwarding. Metoden går ut på at UDP- eller TCP-trafikk med en bestemt mottakerport sendes videre til en spesifikk adresse på lokalnettet. IP-adressene endres fra ruterens eth0-adresse til den lokale tjenestens adresse, og portnummret kan endres om ønskelig. Eksemplet nedenfor viser en port forwarding regel som sender innkommende TCP-pakker med mottakerport 80 videre til adressen 10.10.3.10 og port 8080. En tjeneste på dette endepunktet vil dermed bli gjort tilgjengelig for omverdenen på adressen til eth0-adapteret og port 80. Slike port forwarding-regler legges sammen med de øvrige brannvegg-reglene vist ovenfor i filen /etc/rc.local.

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 (linjen fortsetter)
         -j DNAT --to  10.10.3.10:8080
iptables -A FORWARD -p tcp -d 10.10.3.10 --dport 8080 -j ACCEPT

Tjenester til omverdenen – Dynamisk DNS

Tjenester i lokalnettet kan gjøres tilgjengelig for omverdenen ved hjelp av port forwarding. Da vil du trenge å knytte IP-adressen på eth0-adapteret til et DNS-navn (Internet-navn) slik at andre kan finne frem til tjenesten din. Dette fordrer at du kjøper (eller får gratis) en DNS-tjeneste fra et web-hotell eller lignende. Man kan reservere egne DNS-domener eller registrere seg i et eksisterende, men dette ligger utenfor temaet for denne artikkelen.

Eth0-adapteret får en dynamisk IP-adresse fra Internet-leverandøren som skiftes regelmessig, og det skaper problemer for slik DNS-registrering. Det trengs derfor en mekanisme som oppdaterer DNS-databasen i web-hotellet hver gang eth0 får en endret IP-adresse. Dette besørges av et program som kalles ddclient og som innledningsvis ble installert på RPi.

Programmet ddclient overvåker IP-adressen på eth0 og gir beskjed om endringer til en leverandør av Dynamic DNS, heretter kalt DDNS. Det er flere slike DDNS-leverandører, og noen få tilbyr en enkel gratistjeneste, mens en “premium” tjeneste koster litt penger. I dette eksemplet blir det vist konfigurasjon mot tjenesten noip.com.

# nano /etc/ddclient.conf
- Filen skal ha slikt innhold:
use=web
ssl=yes
protocol=noip
login=<noip brukernavn>
password=<noip password>
<valgt DNS-navn>

# nano /etc/default/ddclient
- Filen skal ha slikt innhold:
run_dhclient=”false”
run_ipup=”false”
run_daemon=”true”
daemon _interval=”300”

- Sett opp ddclient som en kjørende tjeneste i RPi på denne måten:
# sudo service ddclient start

- Jeg er usikker på om denne "pollingen" har en betydning. Man må uansett
  bekrefte adressen manuelt minst månedlig.
# nano /etc/cron.weekly/ddclient
- Filen skal ha dette innholdet
#!/bin/sh
/usr/sbin/ddclient -force

- Sett så filen “executable” med kommandoen
# chmod +x /etc/cron.weekly.ddclient

Konfigurasjon av OpenVPN

Virtual Private Networks (VPN) kan tilby beskyttede koplinger mellom private nettverk, slik at man f.eks. kan bruke arbeidsplassens IT-ressurser hjemmefra eller på reise, at et felles laboratorium kan utnyttes av mange firmaer i fellesskap uten å gi adgang inn i hverandres nettverk. Ikke minst åpner også VPN en mulighet for å nå inn til alle adresser i det lokale nettet fra en hvilken som helst posisjon i Internet slik at de tjenestene som etableres i lokalnettet kan benyttes på reise o.l.

Det finnes flere produkter som kan tilby slike VPN-tjenester, men her vil OpenVPN brukes, fordi det er enklere å konfigurere enn StrongSwan/IPSec. OpenVPN ble installert innledningsvis og noen eksempler på konfigurasjon er omtalt i en annen post.

b. Raspberry Pi som ruter, men uten WiFi

Alternativ (a) slik som beskrevet over gir god kontroll over rutingen, bruk av VPN, egne DNS-navn og diverse tilleggstjenester. Men den har noen ulemper som er nødvendig å leve med:

  • Linux-kjernen er ikke villig til a svitsje mellom WiFi og Ethernet, så tilkoplede klienter vil tilhøre to IP-subnett. Det betyr at trafikk som ikke kan rutes mellom subnettene (f.eks. uPnP og SSDP) vil hindre noen ellers nyttige tjenester.
  • Det er erfaringsmessig en del bugs knyttet til Hostapd-programvaren som skal sette opp WiFi-adapteret i AP-modus.
  • WiFi-adapteret på Raspberry Pi har liten utgangseffekt og ingen utvendig antenne, så det kan ikke påregnes å ha den samme rekkevidden og overføringskapasitet som en egen hjemmeruter.

Om disse ulempene gjør alternativ (a) uakseptabelt kan alternativ (b) brukes, slik det er vist i Figur 1. Her legges ruterfunksjonene til RPi og lokalnettfunksjonene til hjemmeruteren. Ruterfunksjonen i hjemmeruteren benyttes ikke, og RPi’s eth1-adapter koples til en LAN-port på ruteren. DHCP-tjenesten på hjemmeruteren kan ikke kombineres med DHCP-tjenesten i Dnsmasq (på RPi) så én av dem må skrus av. Anbefalingen er å skru av DHCP-tjenesten i hjemmeruteren.

Fordelen med denne konfigurasjonen vil være at alle klienter på lokalnettet (tilkoplet via Ethernet eller WiFi) vil være på samme subnett og kan få DHCP-adresser fra samme adresseområde. Konfigurasjonen av Dnsmasq blir noe forenklet, og all konfigurasjon av WiFi-adapteret på RPi kan sløyfes, inkludert oppsettet av Hostapd. VPN-, NAT- og brannvegg-konfigurasjonen på RPi blir den samme som før.

Alternativ (b) gir den samme fleksibilitet i ruting- og sikkerhetsfunksjonene, og overlater lokalnett-funksjonene til en enhet med flere ethernet-porter og bedre rekkevidde for WiFi-signalet.

c. Rapsberry Pi med ekstra ruterfunksjoner

Alternativ (b) dekker det samme som alternativ (a), men med et mulig gjenstående problem knyttet til ytelse. En Raspberry Pi har begrenset overføringskapasitet, og en mulig forbedring vil være å la hjemmeruteren besørge ruting- brannvegg- og NAT-funksjonene i tillegg til lokalnettfunksjonene slik som i alternativ (b), mens RPi besørger de ønskede ekstratjenestene, som DNS, VPN, web-tjener, filtjener osv.

Alternativ (c) er vist på Figur 1 hvor hjemmeruteren står koplet på sin vanlige plass, og en RPi er tilkoplet på lokalnettsiden. Dette er endringer som må gjøres i konfigurasjonsn av hjemmeruteren:

  • DHCP må skrus av
  • DNS vil ikke være i bruk, og kan skrus av om mulig
  • VPN-trafikk må slippes inn til RPi. Det kreves derfor an port forwaring regel for UDP-trafikk på port 1194
  • Applikasjonstjenester som skal være tilgjengelig fra Internet (utenom en VPN-tunnel) må gis en port forwarding-regel.

RPi får nå den viktige oppgaven med å besørge en DHCP-tjeneste. DHCP-tjenesten forsyner lokale maskiner med IP-adresse/nettmaske, IP-adressen til DNS-tjenesten og evt. ekstra rutinginformasjon.

RPi skal gjennom DHCP-tjenesten sørge for at “vanlig” internet-trafikk (web-surfing, meldingsformidling m.m.) rutes til hjemmeruterens adressse (gjennom opplysninger om default gateway) og dermed ikke bidrar til noen flaskehals i RPi. Trafikk til adresser som inngår i VPN-tunneler blir derimot rutet til RPi, som vedlikeholder alle endepunkter for tunnelene. DNS-tjenesten i RPi (med Dnsmasq) skal lagre internet-navn (dns-navn) med IP-adresser for alle lokale tjenester, samt endepunkter som når gjenom VPN. Forespørsler om andre adresser vidersendes til hjemmeruteren. RPi må også besørge oppdatering av en DDNS-tjeneste når ruterens ytre adresse endres.

Med en slik konfigurasjon vil RPi begrense seg til DHCP-, DDNS- og DNS-tjeneste samt besørge trafikk gjennom VPN-tunnelene. Annen trafikk, samt oppgaver knyttet til NAT-funksjon og brannvegg (som er potensielt ganske krevende) vil betjenes av hjemmeruteren. Konfigurasjonen av RPi vil derfor begrense seg til å konfigurere Dnsmasq for DHCP- og DNS-trafikk, ikke helt ulikt alternativ (a), men med disse forskjellene i dnsmasq.conf:

  • Default gateway skal ikke være DHCP-tjenestens IP-adresse, men hjemmeruterens. Dette må angis eksplisitt med dhcp-option=3,192.168.4.1 (hjemmeruterens lokaladresse)
  • Dersom alle VPN-tunnelene kan samlet i et fåtall subnett, kan disse adressene rutes til RPi (som i dette eksemplet har IP-adresse 192.168.4.2). Lokale maskiner må derfor ha rutinginformasjon for å rute disse subnettene dit. I dnsmasq.conf uttrykkes dette slik: dhcp-option=121,10.10.0.0/16,192.168.4.2 (10.10.0.0/16 er eksempel på VPN-subnett).
  • DHCP-tjenesten trenger nå kun én dhcp-range, siden lokalnettet kun er ett subnett.

Alternativ (c) krever noe mer vedlikehold knyttet til endringer i VPN-konfigurasjonene, idet det kan kreve at ny rutinginformasjon blir utdelt. Alternativ (c) er mindre fleksibelt siden f.eks. brannvegg- og NAT-funksjonene ikke kan konfigureres så fritt som om de besørges av iptables i RPi. Om IP versjon 6 er nødvendig å ta hensyn til, så kan også hjemmeruteren representere en begrensning, siden slike produkter i varierende grad kan behandle IPv6-trafikk.

Oppsummering

Denne artikkelen har forklart hvordan du på ulike måter kan konfigurere en RPi for å videreutvikle hjemmenettverket med flere og bedre tjenester. Tre alternativer er presentert med sine fordeler og ulemper. I tillegg til et forbedret og videreutviklet hjemmenett vil du oppnå mye ferdigheter i bruk av Linux, feilsøking i nettverk og forståelse av prinsippene bak IP-protokollen.

Ekstralesning: