2014-06-16 01:16:14 +00:00
|
|
|
#lang racket/base
|
|
|
|
;; ARP protocol, http://tools.ietf.org/html/rfc826
|
|
|
|
;; Only does ARP-over-ethernet.
|
|
|
|
|
|
|
|
(provide (struct-out arp-query)
|
|
|
|
(struct-out arp-assertion)
|
2014-06-19 01:24:47 +00:00
|
|
|
(struct-out arp-interface)
|
2014-06-16 01:16:14 +00:00
|
|
|
spawn-arp-driver)
|
|
|
|
|
|
|
|
(require racket/set)
|
|
|
|
(require racket/match)
|
|
|
|
(require minimart)
|
|
|
|
(require minimart/drivers/timer)
|
|
|
|
(require minimart/demand-matcher)
|
|
|
|
(require bitsyntax)
|
|
|
|
|
|
|
|
(require "dump-bytes.rkt")
|
2014-06-19 01:24:47 +00:00
|
|
|
(require "configuration.rkt")
|
2014-06-16 01:16:14 +00:00
|
|
|
(require "ethernet.rkt")
|
|
|
|
|
2014-06-19 01:24:47 +00:00
|
|
|
(struct arp-query (protocol protocol-address interface link-address) #:prefab)
|
|
|
|
(struct arp-assertion (protocol protocol-address interface-name) #:prefab)
|
|
|
|
(struct arp-interface (interface-name) #:prefab)
|
2014-06-16 01:16:14 +00:00
|
|
|
|
|
|
|
(define ARP-ethertype #x0806)
|
2014-06-17 16:06:27 +00:00
|
|
|
(define cache-entry-lifetime-msec (* 14400 1000))
|
2014-06-16 01:16:14 +00:00
|
|
|
(define wakeup-interval 5000)
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2014-06-19 01:24:47 +00:00
|
|
|
(define (spawn-arp-driver)
|
|
|
|
(spawn-demand-matcher (arp-interface (?!))
|
|
|
|
#:supply-level 1
|
|
|
|
spawn-arp-interface))
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2014-06-16 01:16:14 +00:00
|
|
|
(struct cache-key (protocol address) #:transparent)
|
2014-06-19 01:24:47 +00:00
|
|
|
(struct cache-value (expiry interface address) #:transparent)
|
|
|
|
|
|
|
|
(struct state (cache queries assertions) #:transparent)
|
|
|
|
|
|
|
|
(define (spawn-arp-interface interface-name)
|
|
|
|
(log-info "spawn-arp-interface ~v" interface-name)
|
|
|
|
(lookup-ethernet-hwaddr (gestalt-for-supply interface-name)
|
|
|
|
interface-name
|
|
|
|
(lambda (hwaddr) (spawn-arp-interface* interface-name hwaddr))))
|
|
|
|
|
|
|
|
(define (gestalt-for-supply interface-name)
|
|
|
|
(sub (arp-interface interface-name) #:level 1))
|
2014-06-16 01:16:14 +00:00
|
|
|
|
2014-06-19 01:24:47 +00:00
|
|
|
(define (spawn-arp-interface* interface-name hwaddr)
|
|
|
|
(log-info "spawn-arp-interface* ~v ~v" interface-name hwaddr)
|
|
|
|
(define interface (ethernet-interface interface-name hwaddr))
|
2014-06-16 01:16:14 +00:00
|
|
|
|
|
|
|
(define (expire-cache cache)
|
|
|
|
(define now (current-inexact-milliseconds))
|
|
|
|
(define (not-expired? v) (< now (cache-value-expiry v)))
|
|
|
|
(for/hash [((k v) (in-hash cache)) #:when (not-expired? v)]
|
|
|
|
(values k v)))
|
|
|
|
|
|
|
|
(define timer-key (list 'arp interface-name))
|
|
|
|
|
|
|
|
(define (set-wakeup-alarm)
|
|
|
|
(send (set-timer timer-key wakeup-interval 'relative)))
|
|
|
|
|
|
|
|
(define (compute-gestalt cache)
|
|
|
|
(gestalt-union (sub (timer-expired timer-key ?))
|
|
|
|
(sub (ethernet-packet-pattern interface-name #t ARP-ethertype))
|
|
|
|
(sub (ethernet-packet-pattern interface-name #t ARP-ethertype) #:level 1)
|
|
|
|
(pub (ethernet-packet-pattern interface-name #f ARP-ethertype))
|
2014-06-19 01:24:47 +00:00
|
|
|
(gestalt-for-supply interface-name)
|
|
|
|
(sub (arp-assertion ? ? interface-name) #:level 1)
|
|
|
|
(pub (arp-query ? ? interface ?) #:level 2)
|
2014-06-16 01:16:14 +00:00
|
|
|
(for/fold [(g (gestalt-empty))] [((k v) (in-hash cache))]
|
|
|
|
(gestalt-union g (pub (arp-query (cache-key-protocol k)
|
|
|
|
(cache-key-address k)
|
2014-06-19 01:24:47 +00:00
|
|
|
(cache-value-interface v)
|
2014-06-16 01:16:14 +00:00
|
|
|
(cache-value-address v)))))))
|
|
|
|
|
2014-06-20 01:56:30 +00:00
|
|
|
(define (build-packet dest-mac ptype oper sender-ha sender-pa target-ha target-pa)
|
2014-06-16 01:16:14 +00:00
|
|
|
(define hlen (bytes-length target-ha))
|
|
|
|
(define plen (bytes-length target-pa))
|
|
|
|
(define packet (bit-string->bytes
|
|
|
|
(bit-string (1 :: integer bytes 2)
|
|
|
|
(ptype :: integer bytes 2)
|
|
|
|
hlen
|
|
|
|
plen
|
|
|
|
(oper :: integer bytes 2)
|
|
|
|
(sender-ha :: binary bytes hlen)
|
|
|
|
(sender-pa :: binary bytes plen)
|
|
|
|
(target-ha :: binary bytes hlen)
|
|
|
|
(target-pa :: binary bytes plen))))
|
2014-06-19 01:24:47 +00:00
|
|
|
(ethernet-packet interface
|
2014-06-16 01:16:14 +00:00
|
|
|
#f
|
2014-06-19 01:24:47 +00:00
|
|
|
hwaddr
|
2014-06-16 01:16:14 +00:00
|
|
|
dest-mac
|
|
|
|
ARP-ethertype
|
|
|
|
packet))
|
|
|
|
|
|
|
|
(define (analyze-incoming-packet source destination body s)
|
|
|
|
(bit-string-case body
|
|
|
|
([ (= 1 :: integer bytes 2)
|
|
|
|
(ptype :: integer bytes 2)
|
|
|
|
hlen
|
|
|
|
plen
|
|
|
|
(oper :: integer bytes 2)
|
|
|
|
(sender-hardware-address0 :: binary bytes hlen)
|
|
|
|
(sender-protocol-address0 :: binary bytes plen)
|
|
|
|
(target-hardware-address0 :: binary bytes hlen)
|
2014-06-19 01:24:47 +00:00
|
|
|
(target-protocol-address0 :: binary bytes plen)
|
2014-06-19 03:58:25 +00:00
|
|
|
(:: binary) ;; The extra zeros exist because ethernet packets
|
|
|
|
;; have a minimum size. This is, in part, why
|
|
|
|
;; IPv4 headers have a total-length field, so
|
|
|
|
;; that the zero padding can be removed.
|
2014-06-19 01:24:47 +00:00
|
|
|
]
|
2014-06-16 01:16:14 +00:00
|
|
|
(let ()
|
|
|
|
(define sender-protocol-address (bit-string->bytes sender-protocol-address0))
|
|
|
|
(define sender-hardware-address (bit-string->bytes sender-hardware-address0))
|
|
|
|
(define target-protocol-address (bit-string->bytes target-protocol-address0))
|
2014-06-20 01:56:30 +00:00
|
|
|
(define learned-key (cache-key ptype sender-protocol-address))
|
|
|
|
(when (and (set-member? (state-queries s) learned-key) ;; it is relevant to our interests
|
|
|
|
(not (equal? sender-hardware-address
|
|
|
|
(cache-value-address (hash-ref (state-cache s)
|
|
|
|
learned-key
|
|
|
|
(lambda ()
|
|
|
|
(cache-value #f #f #f)))))))
|
|
|
|
(log-info "~a ARP Adding ~a = ~a to cache"
|
|
|
|
interface-name
|
|
|
|
(pretty-bytes sender-protocol-address)
|
|
|
|
(pretty-bytes sender-hardware-address)))
|
2014-06-16 01:16:14 +00:00
|
|
|
(define cache (hash-set (expire-cache (state-cache s))
|
2014-06-20 01:56:30 +00:00
|
|
|
learned-key
|
2014-06-16 01:16:14 +00:00
|
|
|
(cache-value (+ (current-inexact-milliseconds)
|
|
|
|
cache-entry-lifetime-msec)
|
2014-06-19 01:24:47 +00:00
|
|
|
interface
|
2014-06-16 01:16:14 +00:00
|
|
|
sender-hardware-address)))
|
|
|
|
(transition (struct-copy state s
|
|
|
|
[cache cache])
|
|
|
|
(list
|
|
|
|
(case oper
|
|
|
|
[(1) ;; request
|
|
|
|
(if (set-member? (state-assertions s)
|
|
|
|
(cache-key ptype target-protocol-address))
|
2014-06-20 03:17:59 +00:00
|
|
|
(begin
|
|
|
|
(log-info "~a ARP answering request for ~a/~a"
|
|
|
|
interface-name
|
|
|
|
ptype
|
|
|
|
(pretty-bytes target-protocol-address))
|
|
|
|
(send (build-packet sender-hardware-address
|
|
|
|
ptype
|
|
|
|
2 ;; reply
|
|
|
|
hwaddr
|
|
|
|
target-protocol-address
|
|
|
|
sender-hardware-address
|
|
|
|
sender-protocol-address)))
|
2014-06-16 01:16:14 +00:00
|
|
|
'())]
|
|
|
|
[(2) '()] ;; reply
|
|
|
|
[else '()])
|
|
|
|
(routing-update (compute-gestalt cache))))))
|
|
|
|
(else #f)))
|
|
|
|
|
2014-06-19 01:24:47 +00:00
|
|
|
(define queries-projection (project-subs #:level 1 (arp-query (?!) (?!) ? ?)))
|
2014-06-16 01:16:14 +00:00
|
|
|
(define (gestalt->queries g)
|
2014-06-19 01:24:47 +00:00
|
|
|
(for/set [(e (in-set (gestalt-project/keys g queries-projection)))]
|
2014-06-16 01:16:14 +00:00
|
|
|
(match-define (list ptype pa) e)
|
|
|
|
(cache-key ptype pa)))
|
|
|
|
|
2014-06-19 01:24:47 +00:00
|
|
|
(define assertions-projection (project-pubs (arp-assertion (?!) (?!) ?)))
|
2014-06-16 01:16:14 +00:00
|
|
|
(define (gestalt->assertions g)
|
2014-06-19 01:24:47 +00:00
|
|
|
(for/set [(e (in-set (gestalt-project/keys g assertions-projection)))]
|
2014-06-16 01:16:14 +00:00
|
|
|
(match-define (list ptype pa) e)
|
|
|
|
(cache-key ptype pa)))
|
|
|
|
|
|
|
|
(define (analyze-gestalt g s)
|
|
|
|
(define new-assertions (gestalt->assertions g))
|
|
|
|
(define added-assertions (set-subtract new-assertions (state-assertions s)))
|
2014-06-20 01:56:30 +00:00
|
|
|
(define new-s (struct-copy state s [queries (gestalt->queries g)] [assertions new-assertions]))
|
2014-06-16 01:16:14 +00:00
|
|
|
(transition new-s
|
|
|
|
(list
|
2014-06-19 01:24:47 +00:00
|
|
|
(when (gestalt-empty? (gestalt-filter g (gestalt-for-supply interface-name)))
|
|
|
|
(quit))
|
|
|
|
(for/list [(a (in-set added-assertions))]
|
|
|
|
(log-info "~a ARP Announcing ~a as ~a"
|
|
|
|
interface-name
|
|
|
|
(pretty-bytes (cache-key-address a))
|
|
|
|
(pretty-bytes hwaddr))
|
2014-06-20 01:56:30 +00:00
|
|
|
(send (build-packet broadcast-ethernet-address
|
2014-06-19 01:24:47 +00:00
|
|
|
(cache-key-protocol a)
|
|
|
|
2 ;; reply -- gratuitous announcement
|
|
|
|
hwaddr
|
|
|
|
(cache-key-address a)
|
|
|
|
hwaddr
|
|
|
|
(cache-key-address a)))))))
|
2014-06-16 01:16:14 +00:00
|
|
|
|
2014-06-20 01:56:30 +00:00
|
|
|
(define (send-questions s)
|
|
|
|
(define unanswered-queries
|
|
|
|
(set-subtract (state-queries s) (list->set (hash-keys (state-cache s)))))
|
|
|
|
(define (some-asserted-pa ptype)
|
|
|
|
(match (filter (lambda (k) (equal? (cache-key-protocol k) ptype))
|
|
|
|
(set->list (state-assertions s)))
|
|
|
|
['() #f]
|
|
|
|
[(list* k _) (cache-key-address k)]))
|
|
|
|
(transition s
|
|
|
|
(for/list [(q (in-set unanswered-queries))]
|
|
|
|
(define pa (some-asserted-pa (cache-key-protocol q)))
|
|
|
|
(log-info "~a ARP Asking for ~a from ~a"
|
|
|
|
interface-name
|
|
|
|
(pretty-bytes (cache-key-address q))
|
|
|
|
(and pa (pretty-bytes pa)))
|
|
|
|
(when pa
|
|
|
|
(send (build-packet broadcast-ethernet-address
|
|
|
|
(cache-key-protocol q)
|
|
|
|
1 ;; request
|
|
|
|
hwaddr
|
|
|
|
pa
|
|
|
|
zero-ethernet-address
|
|
|
|
(cache-key-address q)))))))
|
|
|
|
|
2014-06-16 01:16:14 +00:00
|
|
|
(list (set-wakeup-alarm)
|
|
|
|
(spawn (lambda (e s)
|
2014-06-19 01:24:47 +00:00
|
|
|
;; (log-info "ARP ~a ~a: ~v // ~v" interface-name (pretty-bytes hwaddr) e s)
|
2014-06-16 01:16:14 +00:00
|
|
|
(match e
|
|
|
|
[(routing-update g)
|
2014-06-20 01:56:30 +00:00
|
|
|
(sequence-transitions (analyze-gestalt g s)
|
|
|
|
send-questions)]
|
2014-06-16 01:16:14 +00:00
|
|
|
[(message (ethernet-packet _ _ source destination _ body) _ _)
|
|
|
|
(analyze-incoming-packet source destination body s)]
|
|
|
|
[(message (timer-expired _ _) _ _)
|
|
|
|
(define new-s (struct-copy state s
|
|
|
|
[cache (expire-cache (state-cache s))]))
|
2014-06-20 01:56:30 +00:00
|
|
|
(define new-g (compute-gestalt (state-cache new-s)))
|
|
|
|
(sequence-transitions (transition new-s
|
|
|
|
(list (set-wakeup-alarm)
|
|
|
|
(routing-update new-g)))
|
|
|
|
send-questions)]
|
2014-06-16 01:16:14 +00:00
|
|
|
[_ #f]))
|
2014-06-19 01:24:47 +00:00
|
|
|
(state (hash) (set) (set))
|
2014-06-16 01:16:14 +00:00
|
|
|
(compute-gestalt (hash)))))
|