🚨 Putin is a war criminal. Russians are bombing hospitals. πŸ’”πŸ‡ΊπŸ‡¦

Modem drops internet every now and then? I made a modem rebooter util

My issues are rare enough to warrant switching to another modem, so I just hacked around it by writing a small piece of software that reboots the modem to try to get the connection back up.

It pings a few known IPs to see if the internet is up, and if it’s done for a defined duration, it’ll reboot the modem.

There are drivers for a few different types of modems (since I’m using this at a few different locations) - the drivers are called “garbage”. πŸ—‘οΈ

I wanted the code to be somewhat safe, so:

  • It doesn’t drop my internet connection (by rebooting) because of bugs
  • Or won’t wait long enough after reboot to let the modem reconnect before trying to reboot again.

Code details for nerds

I think the core code is somewhat understandable:

for {
    up := internetupdetector.IsInternetUp(ctx)
	
    previousState := state
	
    if up {
        state = state.Up()
    } else {
        state = state.Down(time.Now())
    }
	
    if state.IsUpDifferentTo(previousState) {
        if up {
            logl.Info.Println("came back UP")
        } else {
            logl.Error.Println("went DOWN")
        }
    }
	
    if up {
        logl.Debug.Println("up")
    } else {
        logl.Info.Printf("down for %s", time.Since(state.wentDownAt))
    }
	
    if state.ShouldReboot(defaultRebootConfig, time.Now()) {
        logl.Info.Println("rebooting modem")
		
        if err := rebooter.Reboot(conf); err != nil {
            logl.Error.Printf("reboot failed: %s", err.Error())
        } else {
            logl.Info.Println("reboot succeeded")
			
            state = state.SuccesfullReboot(time.Now())
        }
    }
	
    select {
    case <-ctx.Done():
        return nil // graceful stop
    case <-time.After(1 * time.Minute):
    }
}

And the ShouldReboot() implementation:

func (s State) ShouldReboot(rc mrtypes.RebootConfig, now time.Time) bool {
    return !s.wentDownAt.IsZero() &&
        now.Sub(s.wentDownAt) > rc.RebootAfterDownFor &&
        now.Sub(s.lastSuccesfullRebootAt) > rc.ModemRecoversIn
}

There’s actually quite good tests for the logic as well. Example:

// reboot should be only possible at 5 minute mark
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(1)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(2)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(3)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(4)))
assert.Assert(t, state.ShouldReboot(defaultRebootConfig, tplus(5)))

// now reboot
state = state.SuccesfullReboot(tplus(5))

// internet keeps being down, but reboot is not possible until "modemRecoversIn"
// from last reboot
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(5)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(6)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(7)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(8)))
assert.Assert(t, !state.ShouldReboot(defaultRebootConfig, tplus(9)))

// another reboot after previous reboot
assert.Assert(t, state.ShouldReboot(defaultRebootConfig, tplus(10)))

p.s. I actually made this some time ago, but I didn’t manage to post about it until now.


comments powered by Disqus