DHCP antics

Dear Lazyweb,

Raspberry Pi4B, Raspbian 10.10. Behavior I want: if ethernet is available use that, otherwise use wifi. Behavior I get: both are up, each with its own IP. How do I make it not bind wifi if eth is working?

Second: I would like the host to always have the same static IP, regardless of whether it chose ethernet or wifi. With a "real" DHCP server I was able to accomplish that like so:

host NAME-a { fixed-address NAME; hardware ethernet MAC-ETH0; }
host NAME-b { fixed-address NAME; hardware ethernet MAC-WIFI; }

But these days my DHCP server is a UniFi UDM Pro 1.11.4, and It seems to insist upon a 1:1 mapping between IPs and MACs. Is there any way to accomplish this eminently sensible thing?

As always, please note:

  • These are two extremely specific questions.
  • Your guesses are not helpful.
  • Your opinion that I should not want this, likewise.

Previously, previously, previously.

Tags: , , ,

32 Responses:

  1. someguy says:

    Are you trying to do the Wifi-XOR-Ethernet thing only at boot, or anytime you might randomly plug in an Ethernet cable to an already-booted Pi? The solutions will be drastically different for each case. The former being a simple-ish script to kill Wi-fi or skip its initialization when Ethernet comes up successfully; the latter involves arcane NetworkManager incantations.

    Sadly it doesn't appear UniFi is capable of multiple MACs being assigned "duplicate" IPs. Seems to be a both-database-columns-marked-unique thing, or the like. As you said, real DHCP servers are fully capable of this. If DHCP isn't a hard requirement, static IP configuration on both interfaces on the Pi side may be an acceptable workaround.

    • jwz says:
      3

      Only at boot is probably fine.

      I was hoping for something more along the lines of "here is the configuration option you overlooked" than "I guess you could write some fragile-ass shell script to look at the current state of the world and apply a hammer to it."

  2. Nolan says:
    1

    You probably want bonding. Active/passive with the ethernet active.

    Wifi and ethernet need to be on the same L2 segment, but that is almost certainly the case.

    • jwz says:
      2

      Those are words.

      • Jay says:
        4

        He's probably talking about this: https://wiki.debian.org/Bonding

        • 1

          Hmm ... bonding eth and wlan. That sounds like a fun experiment for those with experience bonding interfaces. I'd gamble it wouldn't work though, although it would be a neat solution to the problem.

          • Jay says:
            1

            It's literally one of the examples in the document I linked (they call it "laptop mode") ;-)

            To be honest, I'm as surprised as you are :-D

        • jwz says:

          Yeah, that 500 line document clarifies absolutely nothing for me about how to solve my particular problem.

          • David K. says:
            5

            I think he’s on to something here. You create a virtual Ethernet interface, bond0, with both the wired and Wi-Fi devices (however that’s done on raspian), it flip flops per active topology, and you give it a made up MAC address that you can feed into the UDM’s DHCP config. It should solve both problems at once.

      • tfb says:
        4
        From the documentation referenced by Jay this looks to be exactly one of the supported configurations:

        Tie cable and wireless network interfaces (RJ45/WLAN) together to define a single, virtual (i.e. bonding) network interface (e.g. bond0).

        As long as the network cable is connected, its interface (e.g. eth0)  is used for the network traffic. If you pull the RJ45-plug, ifenslave switches over to the wireless interface (e.g. wlan0) transparently, without  any loss of network packages.

        So that would at least be worth trying and a lot better than a nightmare-script answer   If you are using a single DHCP server for both wifi & ethernet then they either are the same layer 2 network or you would know they weren't because you'd have DHCP relays, so they almost certainly are.

        One good thing with this is you'll get the same MAC address for both interfaces so the DHCP thing should also be solved.  It looks like the thing fabricates a new MAC address distinct from the addresses of either interface.  Let's hope that address is stable across reboots (it won't be, will it).

        Disclaimer: I've used bonds in Linux a fair bit,nand it works but never bonds between wifi & ethernet, and always with static IP addresses.

        • Zygo says:

          Part 1 of the solution is changing the hardware address of both interfaces to be the same, so that DHCP works.  Part 2 is some nefarious magic that sends magic packets to inform your bridge hardware that your WiFi interface is down because your Ethernet interface is up, or vice versa.  Part 3 is managing the interfaces on the client side, which Raspbian has done OK for some years now.

          Bonding does part 1 (both devices assume a new MAC address), and emits gratuitous ARP packets to achieve part 2 for you.  Part 3 is setting up the existing network interfaces to join a bonding group.

          Some WiFi hardware doesn't like having its MAC changed (you can try, but it often doesn't work because the firmware blob just says "lol no").  Reassigning the MAC on the Ethernet side to use the WiFi adapter's MAC usually works.  I haven't tried convincing bonding to use the WiFi's MAC but that sounds like a workaround you might need at some point.

          Note this only works if the WiFi and Ethernet are bridged in the router.  If the router is doing something weird that only looks like bridging (e.g. the router itself is running evil scripts to move IP addresses around on its internal interfaces) and you can't turn it off, then this entire idea is not possible, and you need to start over with a different bridge (or router if the bridge is living inside it).

  3. FXB says:

    Current raspberry pi OS is based on Debian 11 (bullseye) at the time of writing. So guessing you want to apply this network setup to an install on microSD card with an existing software setup that would be hassle to repeat ?

    I've done this (dynamic failover with static IP regardless of NIC active) but on much older versions on a Pi 3b+. Will try to apply same config I used to current RPiOS on a Pi4 later tonight to see if it still works and share if it does.

  4. el_brujo says:
    4
    If a bonding-based approach is not what you want you can try mutexing wifi and eth by adding these pre-up/post-down lines to the respective iface stanzas in /etc/network/interfaces (a non-0 exit status will keep the interface down).  This also sets the MAC address of wlan0 to that of eth0.  Both interfaces need to be marked as auto.
    # wlan0
      pre-up if /sbin/ip -oneline link show eth0| grep -q -e NO-CARRIER; then /sbin/ip link set wlan0 address ET:H0:_M:AC:AD:DR; else false; fi
      post-down /sbin/ip link set wlan0 address WL:AN:0M:AC:AD:DR
    
    # eth0
      pre-up if /sbin/ip -oneline link show eth0| grep -q -e NO-CARRIER; then false; else /sbin/ifdown wlan0 || true; fi
    Notes/caveats: interfaces(5) says that calling ifdown from within a pre-up line will not deadlock as long as it refers to a different interface.  The NO-CARRIER test is racy, but should not be a problem.  Unlike the bonding-based approach this does not automatically react to link state changes.  For an event queue of link state changes run this: ip -t monitor link dev eth0
    • jwz says:
      • There is nothing in /etc/network/interfaces or /etc/network/interfaces.d/ so I don't know where this goes.
      • Without some way for the MAC addresses to not be hardcoded per-host, this is a non-starter.
      • el_brujo says:
        1
        Looks like Raspberry Pi OS now by default relies exclusively on systemd+dhcpcd instead of ifupdown for network configuration, probably because it makes working with systemd massively easier for common use cases.  dhcpcd seems to not offer anything like pre-up/post-down.  Unfortunately this means systemd voodoo becomes necessary.

        The challenge is to bring up only one of the interfaces, but still make both interface units succeed as otherwise that might cascade into other units failing/not starting in difficult-to-predict ways.  There is also the issue of restoring the overwritten MAC address of the wifi device.  The following will get auto-merged into whatever is defined in /lib/systemd/system/dhcpcd@.service:

        # /etc/systemd/system/dhcpcd@.service.d/ifmutex.conf
        [Service]
        ExecStart=
        ExecStopPost=
        ExecStart=/bin/sh -c 'if [ %I = eth0 ]; then if /sbin/ip -oneline link show eth0| /bin/grep -q -e NO-CARRIER; then echo $$ >/run/dhcpcd/%I.pid; exec /bin/sleep infinity; else exec /usr/bin/dhcpcd -w -q %I; fi; elif [ %I = wlan0 ]; then if /sbin/ip -oneline link show eth0| /bin/grep -q -e NO-CARRIER; then if [ ! -e /run/wlan0.macaddr ]; then /sbin/ip -br link show wlan0|/bin/cut -c 33-49 > /run/wlan0.macaddr ; fi; /sbin/ip link set wlan0 address $(/sbin/ip -br link show eth0|/bin/cut -c 33-49); exec /usr/bin/dhcpcd -w -q %I; else echo $$ >/run/dhcpcd/%I.pid; exec /bin/sleep infinity; fi; fi'
        ExecStopPost=-/bin/sh -c 'if [ %I = wlan0 -a -e /run/wlan0.macaddr ]; then /sbin/ip link set wlan0 address $(/bin/cat /run/wlan0.macaddr); fi'
        Notes/caveats: I have not tested this, but it should be quite close to working.  The alternative is to either ditch dhcpcd and go all in on systemd and define everything through .link and .network units or to go back to ifupdown.  If you have "jq" installed you can use ip -j to produce JSON and replace the /bin/cut calls with something more robust.
  5. NdrU says:
    2
    I don't have a free raspberry pi to try this, but can you install networkmanager?
    $ cat /etc/NetworkManager/dispatcher.d/99-wlan-auto
    #!/bin/bash
    wired_interfaces="en.*|eth.*"
    if [[ "$1" =~ $wired_interfaces ]]; then
        case "$2" in
            up)
                nmcli radio wifi off
                ;;
            down)
                nmcli radio wifi on
                ;;
        esac
    fi
    
    
    I've had this on my laptop for a while now and it works well, but not sure if raspberry wifi can be controlled by nmcli
    • Duality K. says:
      1

      Seconded.  I have Network Manager running on a Pi Zero W with nmtui (a curses based front end) and it does all right; I have had similarly good luck with a ThinkPad which bounces from wifi to wired with some regularity.

      It's a heavy solution for what it is, but it does work.

    • xrayspx says:
      1
      Ok here it is on the same platform,

      Raspbian 10 didn't ship with NetworkManager it seems.  I started with a fresh image, installed networkmanager & networkmanager-gnome, then used a <a href="https://askubuntu.com/questions/1271491/disable-wifi-if-lan-is-connected">similar script</a>.  This is actually an example script from man nmcli-examples, so I'm not so worried about it being super fragile.

      So on a Raspberry Pi4 (with NetworkManager), this does seem to do what we want.  Boot with electrical, eth0 is the only interface in ifconfig.  Unplug the cable, WiFi starts right up, connects, etc.  Plug the cable back in and it kills WiFi.

      /etc/NetworkManager/dispatcher.d/70-wifi-wired-exclusive.sh

      <code>

      #!/bin/bash
      export LC_ALL=C
      
      enable_disable_wifi ()
      {
          result=$(nmcli dev | grep "ethernet" | grep -w "connected")
          if [ -n "$result" ]; then
              nmcli radio wifi off
          else
              nmcli radio wifi on
          fi
      }
      
      if [ "$2" = "up" ]; then
          enable_disable_wifi
      fi
      
      if [ "$2" = "down" ]; then
          enable_disable_wifi
      fi
      </code>
  6. Adam says:

    On my laptop I have configured both interfaces with the same static ip address and then I use the ifmetric package to prioritize wired over wireless by adding "metric 2" to the wifi section of /etc/network/interfaces and "metric 1" to the wired section.

    Switching over when unplugging the cable is hands off and near seamless.

    /sbin/route outputs a "Metric" column for the various interfaces (nice for checking).

    I was too impatient to follow (and debug) the guides for setting up bonding, so I was happy when I found ifmetric.

    • jwz says:

      Giving them static IPs instead of having them use what was handed out by DHCP is no bueno.

      • Adam says:

        Yes, ifmetric is only an answer to the the first question, switching between ethernet and wifi.

        I have no idea how to solve the second qustion without a "real" dhcpd, so I specificly did not try to answer it.

  7. 3

    You're a perl hacker.

    Write a 20 line script to choose the working gateway. Having the same IP/mac active on multiple interfaces (irrespective of connectivity state) is just bad idea.

  8. jimbobmcgee says:
    Others have mentioned bonding, and some examples have been given in the pre-systemd methodology.

    If Raspbian is now systemd-based, a step-by-step by Kerli Low tracks with how I would personally approach this: https://kerlilow.me/blog/setting-up-systemd-networkd-with-bonding/

    To satisfy the static IP/DHCP reservation, then given the a skim of https://www.freedesktop.org/software/systemd/man/systemd.netdev.html, I would augment Kerli's 10-bond1.netdev file with...

    [Bond]
    FailOverMACPolicy=follow
    Which I believe is supposed to make an active-passive bond artificially 'transfer' whatever its defined MAC address is between the 'slave' interfaces, upon any failover.  

    The MAC address itself can either be fixed...

    [NetDev]
    MACAddress=0e:xx:xx:xx:xx:xx
    ...randomized by systemd/udev...
    [NetDev]
    MACAddress=none
    ...or, by default, i think it steals the MAC of the first-defined 'slave' interface.

    If fixing the MAC address to a custom one, anything where the second hex-digit of the first octet is 2, 6, A or E should not collide with real-world allocations (https://www.blackmanticore.com/fc5c95c7c2e29e262ec89c539852f8fb).

    If Raspbian is not actually systemd-based, then the other answers here should already be on the path of the righteous (FailOverMACPolicy= may become fail_over_mac=).

    If MAC-meddling doesn't work, note that DHCP doesn't actually have to use MAC in its requests, and it can be overridden by a raw string (I think) called a DUID.  There are options in dhclient.conf and/or systemd to have this be a fixed string or generated based on the machine ID.

  9. Carlos says:
    • Your opinion that I should not want this, likewise

    <you shouldn't want this>

    I'm curious[*] how you thought that would work out.

    C.

    [*] Not actually curious.

  10. Matt says:
    2

    ITT: lots of people unfamiliar with lazyweb requests, and haven't kept up on the constantly moving target of network management on linux.

    So, I have the hardware and distro mentioned, tried doing what you want and the answer is pretty definitely that this combination of constraints won't do what you want, by design.  Raspbian activates all available interfaces via DHCPCD (which has the hooks for wpa_supplicant, bypassing any possibilities of at least using something like systemd as hammer to flatten the world).  It's a christmas tree, all you get to do is plug it in.

    There are many variations, but you didn't ask for that.  This link does a good job of a recent summary of the CADT infected decision making process that is network management on Linux (who thought it was a good idea to have separate network management "methodologies" for each combination of use cases?).  Raspbian Buster is systemd, but this particular release (seemingly like Ubuntu and other distros I had handy) all do this same thing, which makes me think everyone decided the common usecase for RPis is just as a desktop device.

    There are a bunch of other potential options, but this set of constraints is a dead-end.

    • xrayspx says:
      1

      Just for my own curiosity, what's the issue with the NetworkManager solution using the sample script they provide which monitors the network and will up/down WiFi based on whether ethernet is plugged in?  It seems to fit the brief and works as expected on a Pi 4 with Raspbian 10 in testing.

      • Matt says:

        Per the comments : 'I was hoping for something more along the lines of "here is the configuration option you overlooked" than "I guess you could write some fragile-ass shell script to look at the current state of the world and apply a hammer to it."'

        What you suggested, to me, fell in the latter category, but you're welcome to research it and publish your findings.  Personally, I don't like or want to deal with NetworkManager since it seems pretty desktop-behavior-centric and that's not my jam.

        • xrayspx says:

          Ah.  Well, given that this particular fragile-ass shell script that is provided and suggested by the NetworkManager man page as the correct way to have WiFi & Electrical auto-switch without having a DE manage it, it might be the most straightforward answer.  I mean, half the OS is fragile-ass shell scripts at the end of the day.

          • Matt says:

            Oh, I get it, but I don't think JWZ is looking for the answer "Well, it's all shit, so might as well deal with it the hard way".  Lazyweb requests are a) lazy and b) requests, which inherently excludes these sorts of solutions that are inherently "do it the hard way".

Leave a Reply

Your email address will not be published. But if you provide a fake email address, I will likely assume that you are a troll, and not publish your comment.

Starting a new top-level thread.

  • Previously