eufon/blinkenlicht/metric/uplink.fnl

118 lines
3.8 KiB
Plaintext
Raw Normal View History

2022-04-01 15:23:14 +00:00
(local nl (require :netlink))
(local view (. (require :fennel) :view))
;; $ grep DEVTYPE /sys/class/net/*/uevent
;; /sys/class/net/docker0/uevent:DEVTYPE=bridge
;; /sys/class/net/wlp4s0/uevent:DEVTYPE=wlan
;; /sys/class/net/wwp0s20f0u2i12/uevent:DEVTYPE=wwan
;; (ethernet and loopback devices don't have DEVTYPE)
2022-04-03 11:41:15 +00:00
(fn devtype [ifname]
(with-open [f (io.open (.. "/sys/class/net/" ifname "/uevent") :r)]
(accumulate [dtype nil
line #(f:read "*l")
:until dtype]
(let [(name value) (line:match "([^=]+)=(.+)")]
(if (= name "DEVTYPE") value dtype)))))
2022-04-01 15:23:14 +00:00
;; if the type is wlan, we can get a signal strength indicator
;; from the "quality - link" column of /proc/net/wireless
2022-04-03 11:41:15 +00:00
(fn wlan-link-quality [ifname]
(with-open [f (io.open "/proc/net/wireless" :r)]
(accumulate [strength nil
line #(f:read "*l")
:until strength]
;; "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
;; "%6d %6d %6d\n",
;; https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/wireless/wext-proc.c#n49
(let [(name status link level)
(line:match "(.-): +(%x+) +(%S-)[ .] +(%S-)[ .]")]
(if (= name ifname)
(tonumber level)
strength)))))
(fn wlan-link-ssid [ifname]
;; could do this directly using an ioctl
;; http://papermint-designs.com/dmo-blog/2016-08-how-to-get-the-essid-of-the-wifi-network-you-are-connected-to-#
(with-open [f (io.popen (.. "iwgetid " ifname " --raw") :r)]
(f:read "*l")))
(fn get-network-info [event]
;; augments a newlink event with relevant information, if it's
;; sufficiently well-configured to have any
2022-04-01 15:23:14 +00:00
2022-04-03 11:41:15 +00:00
;; "up" => administratively up
;; "running" => can actually exchange packets
(when (= event.running "yes")
(let [dtype (devtype event.name)]
(tset event :devtype dtype)
(when (= dtype "wlan")
(if (not event.ssid)
(tset event :ssid (wlan-link-ssid event.name)))
(tset event :quality (wlan-link-quality event.name)))
(when (= dtype "wwan")
;; for wwan, need to determine how to get strength and carrier name
true)
))
2022-04-03 11:41:15 +00:00
event)
2022-04-01 15:23:14 +00:00
(fn uplink []
2022-04-01 15:23:14 +00:00
(let [links {}
routes {}
sock (nl.socket)]
2022-04-01 15:23:14 +00:00
(fn handle-event [event]
(match event
{:event :newlink}
2022-04-03 11:41:15 +00:00
(tset links event.index
(match event.up
"yes" (get-network-info event)
"no" event))
2022-04-01 15:23:14 +00:00
{:event :newroute}
2022-04-07 18:12:45 +00:00
;; there may be >1 route to any given destination,
2022-04-05 21:16:31 +00:00
;; (e.g wwan and wlan both have default route)
2022-04-07 18:12:45 +00:00
;; so we only update the routes table if the
;; metric is lower
2022-04-06 22:26:37 +00:00
(let [dst (or event.dst "default")
existing (. routes dst)]
(if (or (not existing)
(< event.metric existing.metric))
(tset routes dst event)))
2022-04-01 15:23:14 +00:00
))
(each [_ event (ipairs (sock:query ))]
2022-04-01 15:23:14 +00:00
(handle-event event))
{
:refresh #(each [_ event (ipairs (sock:event))]
2022-04-01 15:23:14 +00:00
(handle-event event))
:fd (sock:fd)
:read (fn [self]
(self:refresh)
(let [defaultroute routes.default
interface (and defaultroute
(. links defaultroute.index))]
(and interface (= interface.running "yes")
(get-network-info interface))))
:wait #(sock:poll 1000)
:interface (fn [self ifnum]
(. links ifnum))
2022-04-01 15:23:14 +00:00
}
))
(comment
2022-04-01 15:23:14 +00:00
(let [nl (netlunk)]
(while (or (nl:wait) true)
(nl:refresh)
(match (nl:uplink)
2022-04-03 11:41:15 +00:00
interface
(print "default route through " (view interface))
nil
2022-04-01 15:23:14 +00:00
(print "no default route")
))))
{
:new uplink
}