CS3700-project2/private/iputil.rkt

157 lines
4.6 KiB
Racket

#lang racket
(provide (struct-out peer) (struct-out subnet)
string->ip
ip->string
ip-netmask
netmask-ip
string->subnet
subnet->string
string->peer
subnet->bl
subnet-flip-last
subnet-drop-last
subnet-disaggregate
ip->bl)
(require racket/struct)
;; Support functions for dealing with IP addresses, subnets, and peers
(module+ test
(require rackunit))
;; An IP is a Nat
;; A PeerType is one of
;; - 'prov
;; - 'cust
;; - 'peer
;; A Peer is a (peer IP PeerType)
(struct peer [ip type]
#:transparent
#:methods gen:custom-write
[(define write-proc
(make-constructor-style-printer
(lambda (x) 'peer)
(lambda (x) (list (ip->string (peer-ip x)) (peer-type x)))))])
;; A Subnet is a (subnet IP Nat)
(struct subnet [ip mask]
#:transparent
#:methods gen:custom-write
[(define write-proc
(make-constructor-style-printer
(lambda (x) 'subnet)
(lambda (x) (list (subnet->string x)))))])
;; Str -> IP
;; Parses the IP address contained in str
(define (string->ip str)
(define parts (reverse (string-split str ".")))
(for/sum ([part (in-list parts)]
[i (in-naturals)])
(arithmetic-shift (string->number part)
(* i 8))))
;; IP -> Str
;; Serializes the IP address ip to a string
(define (ip->string ip)
(define parts
(reverse (for/list ([i (in-range 4)])
(number->string (bitwise-and (arithmetic-shift ip (* i -8))
255)))))
(string-join parts "."))
;; IP -> Nat
;; Calculates how many mask bits are set in the given netmask represented as an IP address
;; Example: 255.0.0.0 -> 8
(define (ip-netmask ip)
(- 32
(for/sum ([i (in-range 32)])
#:break (positive? (bitwise-and ip (arithmetic-shift 1 i)))
1)))
;; Nat -> IP
;; Creates an IP address representing the given netmask size
;; Example: 8 -> 255.0.0.0
(define (netmask-ip mask)
(arithmetic-shift (sub1 (arithmetic-shift 1 mask))
(- 32 mask)))
;; Str -> Subnet
;; Parses a subnet from string
(define (string->subnet str)
(match-define (list ipstr maskstr) (string-split str "/"))
(subnet (string->ip ipstr)
(string->number maskstr)))
;; Subnet -> Str
;; Serializes a subnet to string
(define (subnet->string sub)
(format "~a/~a"
(ip->string (subnet-ip sub))
(subnet-mask sub)))
;; Subnet -> [Listof Bool]
;; Converts a subnet into a list of booleans representing its bits
(define (subnet->bl sub)
(ip->bl (subnet-ip sub) (subnet-mask sub)))
;; [Listof Bool] -> [Listof Bool]
;; Flips the last bit of the given bit list
(define (bl-flip-last bl)
;; cursed line
(match bl
[`(,f ... ,l) `(,@f ,(not l))]))
;; Subnet -> Subnet
;; Provides a subnet with the last bit flipped
(define (subnet-flip-last sub)
(subnet (bitwise-xor (arithmetic-shift 1 (- 32 (subnet-mask sub))) (subnet-ip sub))
(subnet-mask sub)))
(define (subnet-drop-last sub)
(match-define (subnet ip mask) sub)
(subnet (arithmetic-shift (arithmetic-shift ip (- mask 33)) (- 33 mask))
(sub1 mask)))
;; Subnet Subnet -> [Listof Subnet]
;; Given an aggregate subnet and a component part, return an optimal list of
;; subnets filling the address space represented by (agg - part)
(define (subnet-disaggregate agg part)
(define diff (- (subnet-mask part) (subnet-mask agg)))
(define part-flip (subnet-flip-last part))
(cond
[(= 1 diff) (list part-flip)]
[else (cons part-flip (subnet-disaggregate agg (subnet-drop-last part)))]))
;; IP -> [Listof Bool]
;; Converts an IP address into a list of booleans representing its bits
(define (ip->bl ip [bits 32])
(for/list ([i (in-range bits)])
(= 1 (bitwise-and 1 (arithmetic-shift ip (- i 31))))))
;; Str -> Peer
;; Parses a peer from string
(define (string->peer str)
(match-define (list ip type) (string-split str "-"))
(peer (string->ip ip) (string->symbol type)))
;; ================================================================================
(module+ test
(check-equal? (ip->string (string->ip "123.84.0.67")) "123.84.0.67")
(check-equal? (ip->string (string->ip "0.0.0.0")) "0.0.0.0")
(check-equal? (ip->string (netmask-ip 23)) "255.255.254.0")
(check-equal? (ip->string (netmask-ip 8)) "255.0.0.0")
(check-equal? (ip-netmask (string->ip "255.255.254.0")) 23)
(check-equal? (ip-netmask (string->ip "255.0.0.0")) 8)
(check-equal? (subnet->bl (string->subnet "3.3.0.0/24"))
'(#f #f #f #f #f #f #t #t
#f #f #f #f #f #f #t #t
#f #f #f #f #f #f #f #f)))