eufon

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

uplink.fnl (3895B)


      1 (local nl (require :netlink))
      2 (local view (. (require :fennel) :view))
      3 
      4 ;; $ grep DEVTYPE /sys/class/net/*/uevent
      5 ;; /sys/class/net/docker0/uevent:DEVTYPE=bridge
      6 ;; /sys/class/net/wlp4s0/uevent:DEVTYPE=wlan
      7 ;; /sys/class/net/wwp0s20f0u2i12/uevent:DEVTYPE=wwan
      8 ;; (ethernet and loopback devices don't have DEVTYPE)
      9 
     10 (fn devtype [ifname]
     11   (with-open [f (io.open (.. "/sys/class/net/" ifname "/uevent") :r)]
     12     (accumulate [dtype nil
     13                  line #(f:read "*l")
     14                  :until dtype]
     15                 (let [(name value) (line:match "([^=]+)=(.+)")]
     16                   (if (= name "DEVTYPE") value dtype)))))
     17 
     18 ;; if the type is wlan, we can get a signal strength indicator
     19 ;; from the  "quality - link" column of /proc/net/wireless
     20 
     21 (fn wlan-link-quality [ifname]
     22   (with-open [f (io.open "/proc/net/wireless" :r)]
     23     (accumulate [strength nil
     24                  line #(f:read "*l")
     25                  :until strength]
     26                 ;; "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
     27                 ;; "%6d %6d   %6d\n",
     28                 ;; https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/wireless/wext-proc.c#n49
     29 
     30                 (let [(name status link level)
     31                       (line:match "(.-): +(%x+) +(%S-)[ .] +(%S-)[ .]")]
     32                   (if (= name ifname)
     33                       (tonumber level)
     34                       strength)))))
     35 
     36 (fn wlan-link-ssid [ifname]
     37   ;; could do this  directly using an ioctl
     38   ;; http://papermint-designs.com/dmo-blog/2016-08-how-to-get-the-essid-of-the-wifi-network-you-are-connected-to-#
     39   (with-open [f (io.popen (.. "iwgetid  " ifname " --raw") :r)]
     40     (f:read "*l")))
     41 
     42 (fn get-network-info [event]
     43   ;; augments a newlink event with relevant information, if it's
     44   ;; sufficiently well-configured to have any
     45 
     46   ;; "up" => administratively up
     47   ;; "running" => can actually exchange packets
     48   (when (= event.running "yes")
     49     (let [dtype (devtype event.name)]
     50       (tset event :devtype dtype)
     51       (when (= dtype "wlan")
     52         (if (not event.ssid)
     53             (tset event :ssid (wlan-link-ssid event.name)))
     54         (tset event :quality (wlan-link-quality event.name)))
     55       (when (= dtype "wwan")
     56         ;; for wwan, need to determine how to get strength and carrier name
     57         true)
     58       ))
     59   event)
     60 
     61 (fn uplink []
     62   (let [links {}
     63         routes {}
     64         sock (nl.socket)]
     65     (fn handle-event [event]
     66       (match event
     67         {:event :newlink}
     68         (tset links event.index
     69               (match event.up
     70                 "yes" (get-network-info event)
     71                 "no" event))
     72 
     73         {:event :newroute}
     74         ;; there may be >1 route to any given destination,
     75         ;; (e.g wwan and wlan both have default route)
     76         ;; so we only update the routes table if the
     77         ;; metric is lower
     78         (let [dst (or event.dst "default")
     79               existing (. routes dst)]
     80           (if (or (not existing)
     81                   (< event.metric existing.metric))
     82               (tset routes dst event)))
     83         ))
     84     (each [_ event (ipairs (sock:query ))]
     85       (handle-event event))
     86 
     87     {
     88      :refresh #(each [_ event (ipairs (sock:event))]
     89                  (handle-event event))
     90      :fd (sock:fd)
     91      :read (fn [self]
     92              (self:refresh)
     93              (let [defaultroute routes.default
     94                    interface (and defaultroute
     95                                   (. links defaultroute.index))]
     96                (and interface (= interface.running "yes")
     97                     (get-network-info interface))))
     98      :wait #(sock:poll 1000)
     99      :interface (fn [self ifnum]
    100                   (. links ifnum))
    101      }
    102     ))
    103 
    104 (comment
    105 (let [nl (netlunk)]
    106   (while (or (nl:wait) true)
    107     (nl:refresh)
    108     (match (nl:uplink)
    109       interface
    110       (print "default route through " (view interface))
    111       nil
    112       (print "no default route")
    113       ))))
    114 
    115 {
    116  :new uplink
    117  }