Common Network Configurations in IoT

Hello, everyone! I am Lao Kou! Let’s learn about network configurations together.

Due to work requirements, I need to familiarize myself with the network configurations of gateway devices, so I organized this for easy reference.

I still remember when I was in college, my teacher taught us about computer networks, but I wasn’t paying attention and used the time to learn Java. Therefore, I am not familiar with computer networks, which indirectly led to me being at a loss when encountering network-related issues at work. It was only through reviewing materials and communicating with colleagues that I figured things out. What I wrote here should be of some reference value for beginners.

“The past cannot be admonished, but the future can still be pursued.” This quote comes from the Analects, meaning that past events cannot be advised or changed, but future events can still be amended. Many things need to be personally experienced and cannot be avoided; this may be the fate that is destined! So, rather than avoiding it, it is better to face it calmly, confront your bleak life, and live out your own brilliance!

In today’s rapidly developing technology, almost everyone uses smartphones, and whenever a smartphone connects to the internet, it requires a network.

1. Common Network Configuration Terms

1. IP Address

An IP address is like your home’s “house number” on the internet; with it, data can accurately find your device and transmit information.

Common forms

  • IPv4: Four groups of numbers, such as <span>192.168.1.1</span> (limited in quantity, similar to phone numbers running out).
  • IPv6: Longer, such as <span>2001:0db8:85a3::8a2e:0370:7334</span> (solving the shortage issue, the future mainstream).

2. Gateway

A gateway is like your home’s “front door“; all devices wanting to connect to the external internet (like watching videos or browsing the web) must pass through it to enter or exit.

The gateway can unify all your home devices’ “internal IPs” (like <span>192.168.1.18</span>) into a single “public IP“, protecting privacy and saving addresses.

Subnet Mask

A subnet mask is like a “ruler“, used to measure which part of the IP address is the “community name” (network area) and which part is the “house number” (specific device), allowing the router to know whether to deliver locally or send to another province.

For example: <span>192.168.1.18/24</span>

IP Address:<span>192.168.1.18</span>

Subnet Mask:<span>255.255.255.0</span>

Subnet Mask Length:<span>24</span>

<span>Question: Why is the subnet mask 255.255.255.0?</span>

Common Network Configurations in IoT

MAC Address

A MAC address is the “ID number” of a device’s network card (globally unique and unchangeable), used to accurately identify each phone/computer/printer in a local network (like your home Wi-Fi) for “face-to-face” direct communication.

2. Hands-on Practice

Let’s modify the network configuration using code. I use Go for development, and the operating system is Ubuntu, so I will use Go and Ubuntu as examples!

The network configuration path for Ubuntu is <span>/etc/netplan/01-network-manager-all.yaml</span>, and different Ubuntu versions may have slightly different names, but it does not affect the configuration usage.

Let’s learn about the parameters of network configuration.

Note: If the gateway device has two <span>LAN</span> ports, you can configure two [<span>LAN1</span> and <span>LAN2</span>], corresponding to network interface names <span>eth0</span> and <span>eth1</span>.

Note:<span>Ethernet</span> can <span>dynamically obtain IP</span> [<span>Dhcp</span>] and <span>statically configure IP</span> [<span>Static</span>].

Field Descriptions

Field Description
<span>version</span> Must be <span>2</span> (Netplan configuration version)
<span>renderer</span> Specifies which backend to use, commonly <span>networkd</span> or <span>NetworkManager</span>
<span>ethernets</span> Configures physical network cards
<span>wifis</span> Configures wireless interfaces (requires <span>NetworkManager</span> support)

Example of Ethernet Parameter Configuration

Parameter Type Example Description
<span>dhcp4</span> bool <span>true</span> / <span>false</span> Whether to enable IPv4 DHCP
<span>dhcp6</span> bool <span>true</span> / <span>false</span> Whether to enable IPv6 DHCP
<span>addresses</span> list <span>[192.168.1.100/24]</span> Assign static IP address and subnet mask
<span>gateway4</span> string <span>192.168.1.1</span> IPv4 default gateway
<span>gateway6</span> string <span>fe80::1</span> IPv6 default gateway
<span>nameservers.addresses</span> list <span>[8.8.8.8, 1.1.1.1]</span> DNS server list
<span>nameservers.search</span> list <span>[example.com]</span> DNS search domain list
<span>optional</span> bool <span>true</span> Set as optional (does not affect system startup)
<span>macaddress</span> string <span>aa:bb:cc:dd:ee:ff</span> Specify the MAC address of the network card
<span>mtu</span> int <span>1500</span> Set MTU value
<span>routes.to</span> list <span>10.0.0.0/8</span> Target subnet
<span>routes.via</span> list <span>192.168.1.1</span> Next hop address (gateway), packets will be sent to this gateway for forwarding
<span>routes.metric</span> list <span>100</span> Routing priority, the smaller the number, the higher the priority. Among multiple matching paths, the one with the lower metric will be preferred

Example of Wi-Fi Parameter Configuration

Parameter Type Example Description
<span>dhcp4</span> boolean <span>true</span> / <span>false</span> Whether to enable IPv4 DHCP
<span>dhcp6</span> boolean <span>true</span> / <span>false</span> Whether to enable IPv6 DHCP
<span>optional</span> boolean <span>true</span> Mark the interface as “non-critical”, not blocking on startup
<span>addresses</span> list <span>[192.168.1.100/24]</span> Assign static IP address
<span>gateway4</span> string <span>192.168.1.1</span> Set IPv4 default gateway
<span>gateway6</span> string <span>fe80::1</span> Set IPv6 default gateway
<span>nameservers.addresses</span> list <span>[8.8.8.8, 1.1.1.1]</span> Set DNS servers
<span>macaddress</span> string <span>aa:bb:cc:dd:ee:ff</span> Set MAC address (MAC Spoofing)
<span>routes.to</span> list <span>10.0.0.0/8</span> Target subnet
<span>routes.via</span> list <span>192.168.1.1</span> Next hop address (gateway), packets will be sent to this gateway for forwarding
<span>routes.metric</span> list <span>100</span> Routing priority, the smaller the number, the higher the priority. Among multiple matching paths, the one with the lower metric will be preferred
<span>access-points.<SSID>.password</span> string <span>"12345678"</span> Wi-Fi password, supports WPA/WPA2
<span>access-points.<SSID>.mode</span> string <span>"infrastructure"</span> / <span>"adhoc"</span> Connection mode (infrastructure or point-to-point)
<span>access-points.<SSID>.bssid</span> string <span>aa:bb:cc:dd:ee:ff</span> MAC address of the specific AP to connect to (used only in multi-AP scenarios)
<span>access-points.<SSID>.band</span> string <span>"2.4GHz"</span> / <span>"5GHz"</span> Frequency band (optional)
<span>access-points.<SSID>.channel</span> int <span>6</span> Channel (used only for Ad-Hoc)
<span>access-points.<SSID>.hidden</span> boolean <span>true</span> Whether the SSID is a hidden Wi-Fi

net.go

package main

import (
    "errors"
    "os"
    "os/exec"
    "runtime"
    "strings"

    "gopkg.in/yaml.v3"
)

const (
    // Default network configuration file path
    DEFAULT_NETPLAN_CONFIG_PATH = "/etc/netplan/01-network-manager-all.yaml"
    // LAN1 port 1
    LAN1 = "eth0"
    // LAN2 port 2
    LAN2 = "eth1"
)

type NetworkConfig struct {
    Address    string `json:"address"`
    Gateway    string `json:"gateway"`
    Dns        string `json:"dns"`
    MacAddress string `json:"macAddress"`
}

type NetPlanConfig struct {
    Network struct {
       // Version must be 2, indicating the version of Netplan configuration
       Version int8 `yaml:"version,omitempty"`
       // Renderer specifies which backend to use, commonly networkd or NetworkManager
       // NetworkManager is suitable for desktop environments
       // networkd is suitable for server environments
       Renderer string `yaml:"renderer,omitempty"`
       // Ethernets is a map, with keys as network card names and values as Ethernet configurations
       // For example: `"eth0": { ... }`
       // The network card names can be physical or virtual
       // For example: `"eth0"`, `"eth1"`, `"wlan0"`, etc.
       Ethernets map[string]*Ethernet `yaml:"ethernets,omitempty"`
       // If you need to configure wireless network cards, use the `wifis` field
       Wifis map[string]*Wifi `yaml:"wifis,omitempty"`
    } `yaml:"network,omitempty"`
}

// Wifi wireless network interface configuration structure【Wi-Fi】
type Wifi struct {
    // DHCP4 and DHCP6 indicate whether to enable IPv4 and IPv6 DHCP respectively
    Dhcp4 bool `yaml:"dhcp4"`
    Dhcp6 bool `yaml:"dhcp6,omitempty"`
    // Optional indicates whether this interface is optional; set to true to not block on startup
    Optional bool `yaml:"optional,omitempty"`
    // Address configures the IP address
    Addresses []string `yaml:"addresses,omitempty"`
    // Gateway4 and Gateway6 indicate the default gateways for IPv4 and IPv6 respectively
    Gateway4 string `yaml:"gateway4,omitempty"`
    Gateway6 string `yaml:"gateway6,omitempty"`
    // Nameservers contains DNS server addresses
    Nameservers struct {
       // Addresses is the list of DNS server addresses
       Addresses []string `yaml:"addresses,omitempty"`
    } `yaml:"nameservers,omitempty"`
    // MacAddress is the MAC address of the network card
    MacAddress string `yaml:"macaddress,omitempty"`
    // Routes is a list of routes, each containing a target address, gateway, and priority
    Routes Route `yaml:"routes,omitempty"`
    // AccessPoints is a map, with keys as SSIDs and values as AccessPoint configurations
    AccessPoints map[string]AccessPoint `yaml:"access-points,omitempty"`
}

// Ethernet network card structure【Ethernet】
type Ethernet struct {
    // DHCP4 and DHCP6 indicate whether to enable IPv4 and IPv6 DHCP respectively
    DHCP4 bool `yaml:"dhcp4"`
    DHCP6 bool `yaml:"dhcp6,omitempty"`
    // Addresses is an array of strings representing assigned static IP addresses and subnet masks
    Addresses []string `yaml:"addresses,omitempty"`
    // Gateway4 and Gateway6 indicate the default gateways for IPv4 and IPv6 respectively
    Gateway4 string `yaml:"gateway4,omitempty"`
    Gateway6 string `yaml:"gateway6,omitempty"`
    // Nameservers contains DNS server addresses and search domains
    Nameservers struct {
       // Addresses is the list of DNS server addresses
       Addresses []string `yaml:"addresses,omitempty"`
       // Search is the list of DNS search domains
       Search []string `yaml:"search,omitempty"`
    } `yaml:"nameservers,omitempty"`
    // Routes is a list of routes, each containing a target address, gateway, and priority
    Routes Route `yaml:"routes,omitempty"`
    // Optional indicates whether this interface is optional; set to true to not block on startup
    Optional bool `yaml:"optional,omitempty"`
    // MacAddress is the MAC address of the network card
    MacAddress string `yaml:"macaddress,omitempty"`
    // Mtu is the MTU of the network card
    Mtu int `yaml:"mtu,omitempty"`
}

// Route structure
 type Route struct {
    // To is the target address or subnet
    To string `yaml:"to,omitempty"`
    // Via is the gateway address for the route
    Via string `yaml:"via,omitempty"`
    // Metric is the routing priority; the smaller the number, the higher the priority
    Metric int `yaml:"metric,omitempty"`
}

// AccessPoint wireless access point configuration structure
 type AccessPoint struct {
    // Mode is the connection mode, possible values are "infrastructure" (infrastructure mode) or "adhoc" (point-to-point mode)
    Mode string `yaml:"mode,omitempty"`
    // Password is the Wi-Fi password, supports WPA/WPA2
    Password string `yaml:"password,omitempty"`
    // Bssid is the MAC address of the access point, usually used to connect to a specific AP
    Bssid string `yaml:"bssid,omitempty"`
    // Band is the wireless frequency band, possible values are "2.4GHz" or "5GHz"
    Band string `yaml:"band,omitempty"`
    // Channel is the wireless channel, usually used for Ad-Hoc mode
    Channel int `yaml:"channel,omitempty"`
    // Hidden indicates whether the SSID is hidden, usually used for Ad-Hoc mode
    Hidden bool `yaml:"hidden,omitempty"`
}

func NewDefaultNetPlanConfig() *NetPlanConfig {
    netConfig := &NetPlanConfig{}
    netConfig.Network.Version = 2
    netConfig.Network.Renderer = "NetworkManager"
    // Two network ports
    netConfig.Network.Ethernets = make(map[string]*Ethernet, 2)
    netConfig.Network.Ethernets[LAN1] = &Ethernet{}
    netConfig.Network.Ethernets[LAN2] = &Ethernet{}
    return netConfig
}

func SaveNetPlanConfig(buf []byte, path string) error {
    err := os.WriteFile(path, buf, 0644)
    if err != nil {
       return errors.New("Failed to write network configuration file, error message: " + err.Error())
    }
    return nil
}

func ApplyNetPlanConfig() error {
    err := validateOS()
    if err != nil {
       return err
    }
    err = exec.Command("netplan", "apply").Run()
    if err != nil {
       return errors.New("Failed to apply network configuration, error message: " + err.Error())
    }
    return nil
}

func ReadNetPlanConfig(path string) (*NetPlanConfig, error) {
    data, err := os.ReadFile(path)
    if err != nil {
       return nil, errors.New("Failed to read network configuration file, error message: " + err.Error())
    }
    netConfig := &NetPlanConfig{}
    err = yaml.Unmarshal(data, netConfig)
    if err != nil {
       return nil, errors.New("Failed to deserialize network configuration, error message: " + err.Error())
    }
    return netConfig, nil
}

func GetMacAddress() (string, error) {
    err := validateOS()
    if err != nil {
       return "", err
    }
    output, err := exec.Command("sh", "-c", `ip link show eth0 | awk '/ether/ {print $2}'`).Output()
    if err != nil {
       return "", errors.New("Failed to get MAC address, error message: " + err.Error())
    }
    return strings.TrimSpace(string(output)), nil
}

func GetIpAddress() (string, error) {
    err := validateOS()
    if err != nil {
       return "", err
    }
    output, err := exec.Command("sh", "-c", `ip addr show eth0 | grep "inet " | awk '{print $2}' | cut -d'/' -f1`).Output()
    if err != nil {
       return "", errors.New("Failed to get IP address, error message: " + err.Error())
    }
    return strings.TrimSpace(string(output)), nil
}

func GetGateway() (string, error) {
    err := validateOS()
    if err != nil {
       return "", err
    }
    output, err := exec.Command("sh", "-c", `ip route | grep default | grep eth0 | awk '{print $3}'`).Output()
    if err != nil {
       return "", errors.New("Failed to get gateway, error message: " + err.Error())
    }
    return strings.TrimSpace(string(output)), nil
}

func GetNetmask() (string, error) {
    err := validateOS()
    if err != nil {
       return "", err
    }
    output, err := exec.Command("sh", "-c", `ip addr show eth0 | grep "inet " | awk '{print $2}' | cut -d'/' -f2`).Output()
    if err != nil {
       return "", errors.New("Failed to get subnet mask length, error message: " + err.Error())
    }
    return strings.TrimSpace(string(output)), nil
}

func GetNetworkConfig(path string) (*NetworkConfig, error) {
    config, err := getNetPlanConfig(path)
    if err != nil {
       return nil, err
    }
    ethernet := config.Network.Ethernets[LAN1]
    return &NetworkConfig{
       Address:    ethernet.Addresses[0],
       Gateway:    ethernet.Gateway4,
       Dns:        strings.Join(ethernet.Nameservers.Addresses, ","),
       MacAddress: ethernet.MacAddress,
    }, nil
}

func getNetPlanConfig(path string) (*NetPlanConfig, error) {
    data, err := os.ReadFile(path)
    if err != nil {
       return nil, errors.New("Failed to get network configuration, error message: " + err.Error())
    }
    netConfig := &NetPlanConfig{}
    err = yaml.Unmarshal(data, netConfig)
    return netConfig, nil
}

func validateOS() error {
    if runtime.GOOS != "linux" {
       return errors.New("Gateway does not support this operation on " + runtime.GOOS + " operating system")
    }
    return nil
}

net_test.go

package main

import (
    "fmt"
    "gopkg.in/yaml.v3"
    "testing"
)

func TestNetPlanConfig(t *testing.T) {
    netConfig := NewDefaultNetPlanConfig()
    netConfig.Network.Ethernets[LAN1].DHCP4 = true
    netConfig.Network.Ethernets[LAN1].MacAddress = "22:03:xz:2f:a2:1a"
    netConfig.Network.Ethernets[LAN2].DHCP4 = false
    netConfig.Network.Ethernets[LAN2].MacAddress = "22:03:xz:2f:a2:1a"
    netConfig.Network.Ethernets[LAN2].Addresses = []string{"100.100.1.10/24"}
    netConfig.Network.Ethernets[LAN2].Gateway4 = "100.100.1.1"
    netConfig.Network.Ethernets[LAN2].Nameservers.Addresses = []string{"8.8.8.8", "114.114.114.114"}
    out, err := yaml.Marshal(netConfig)
    if err != nil {
       fmt.Println(err.Error())
    }
    fmt.Println(string(out))
    err = SaveNetPlanConfig(out, "ethernets.yaml")
    if err != nil {
       fmt.Println(err.Error())
    }
    err = ApplyNetPlanConfig()
    if err != nil {
       fmt.Println(err.Error())
    }
    config, err := ReadNetPlanConfig("ethernets.yaml")
    if err != nil {
       fmt.Println(err.Error())
       return
    }
    fmt.Println(config.Network.Ethernets[LAN1].DHCP4)
    config.Network.Ethernets[LAN1].DHCP4 = false
    config.Network.Ethernets[LAN1].Addresses = []string{"192.168.1.10/24"}
    config.Network.Ethernets[LAN1].Gateway4 = "192.168.1.1"
    config.Network.Ethernets[LAN1].Nameservers.Addresses = []string{"8.8.8.8", "114.114.114.114"}
    out, err = yaml.Marshal(config)
    err = SaveNetPlanConfig(out, "ethernets.yaml")
    if err != nil {
       fmt.Println(err.Error())
       return
    }
    fmt.Println(GetNetmask())
    fmt.Println(GetMacAddress())
    fmt.Println(GetIpAddress())
    fmt.Println(GetGateway())
    networkConfig, err := GetNetworkConfig("ethernets.yaml")
    if err != nil {
       fmt.Println(err.Error())
       return
    }
    fmt.Println(networkConfig.Dns)
    fmt.Println(networkConfig.Gateway)
    fmt.Println(networkConfig.Address)
    fmt.Println(networkConfig.MacAddress)
}

Run Results

network:
    version: 2
    renderer: NetworkManager
    ethernets:
        eth0:
            dhcp4: true
            macaddress: 22:03:xz:2f:a2:1a
        eth1:
            dhcp4: false
            addresses:
                - 100.100.1.10/24
            gateway4: 100.100.1.1
            nameservers:
                addresses:
                    - 8.8.8.8
                    - 114.114.114.114
            macaddress: 22:03:xz:2f:a2:1a

The gateway does not support this operation on the Windows operating system.
true
The gateway does not support this operation on the Windows operating system.
The gateway does not support this operation on the Windows operating system.
The gateway does not support this operation on the Windows operating system.
The gateway does not support this operation on the Windows operating system.
8.8.8.8,114.114.114.114
192.168.1.1
192.168.1.10/24
22:03:xz:2f:a2:1a

Processor architectures are divided into <span>amd64</span> and <span>arm64</span>, so there are some differences in Go packaging.

We can check the processor architecture using the <span>arch</span> command.

The gateway devices I have debugged include <span>armv7l</span><span>aarch64</span><span>x86_64</span> among others.

For <span>armv7l</span><span>32-bit</span>】, we can only modify it through commands, using <span>Go</span> to execute commands to modify the configuration.

<span>Static IP Configuration</span>

# IP Address: 192.168.1.18/24
# Gateway: 192.168.1.1
# DNS: 8.8.8.8
nmcli connection delete "eth0 static"
nmcli connection add con-name "eth0 static" ifname eth0 type ethernet
nmcli connection modify "eth0 static" ipv4.method manual ipv4.addresses 192.168.1.18/24 ipv4.gateway 192.168.1.1 ipv4.dns 8.8.8.8
nmcli connection up "eth0 static"
nmcli connection delete "eth0 dhcp"

<span>Dynamic IP Configuration</span>

nmcli connection delete "eth0 dhcp"
nmcli connection add con-name "eth0 dhcp" ifname eth0 type ethernet
nmcli connection modify "eth0 dhcp" ipv4.method auto
nmcli connection up "eth0 dhcp"
nmcli connection delete "eth0 static"

I am Lao Kou, see you next time!

Leave a Comment