2019-12-03 16:47:38 +00:00
|
|
|
package dhcpv4
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"github.com/fatih/color"
|
|
|
|
"github.com/google/gopacket/layers"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
watchedOpts = []layers.DHCPOpt{
|
|
|
|
layers.DHCPOptSubnetMask, // Option 1
|
|
|
|
layers.DHCPOptRouter, // Option 3
|
|
|
|
layers.DHCPOptDNS, // Option 6
|
|
|
|
layers.DHCPOptBroadcastAddr, // Option 28
|
|
|
|
layers.DHCPOptNTPServers, // Option 42
|
|
|
|
layers.DHCPOptLeaseTime, // Option 51
|
|
|
|
layers.DHCPOptT1, // Option 58
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2019-12-09 11:14:01 +00:00
|
|
|
// Generates the summary of relevant DHCP options
|
|
|
|
func (p *Protocol) generateNetworkSummary() string {
|
2020-12-14 13:27:49 +00:00
|
|
|
subnetMask, subnetAvail := formatIP(p.networkSetup[layers.DHCPOptSubnetMask])
|
|
|
|
broadcastAddr, broadcastAvail := formatIP(p.networkSetup[layers.DHCPOptBroadcastAddr])
|
|
|
|
routerAddr, routerAvail := formatIP(p.networkSetup[layers.DHCPOptRouter])
|
|
|
|
dnsAddr, dnsAvail := formatIP(p.networkSetup[layers.DHCPOptDNS])
|
|
|
|
ntpAddr, ntpAvail := formatIP(p.networkSetup[layers.DHCPOptNTPServers])
|
|
|
|
leaseTime, leaseAvail := formatDate(p.networkSetup[layers.DHCPOptLeaseTime])
|
|
|
|
renewalTime, renewalAvail := formatDate(p.networkSetup[layers.DHCPOptT1])
|
|
|
|
|
|
|
|
// Check if there even are any values
|
|
|
|
if !subnetAvail && !broadcastAvail && !routerAvail && !dnsAvail && !ntpAvail && !leaseAvail && !renewalAvail {
|
|
|
|
// No, do not return any summary. This will lead to a collapsed section.
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
summary := fmt.Sprintf("Subnet Mask: %s\nBroadcast: %s\nRouter: %s\nDNS Server: %s\nNTP Server: %s\nLease Time: %s\nRenewal Time: %s", subnetMask, broadcastAddr, routerAddr, dnsAddr, ntpAddr, leaseTime, renewalTime)
|
2019-12-09 11:14:01 +00:00
|
|
|
return summary
|
|
|
|
}
|
|
|
|
|
2019-12-03 16:47:38 +00:00
|
|
|
// Looks for information specifying the setup of the network. This includes
|
|
|
|
// - Option 1: Subnet Mask
|
|
|
|
// - Option 3: Router address
|
|
|
|
// - Option 6: Domain Name Server address
|
|
|
|
// - Option 28: Broadcast address
|
|
|
|
// - Option 42: NTP Server address
|
|
|
|
// - Option 51: IP Address Lease time
|
|
|
|
// - Option 58: IP Renewal time
|
2019-12-09 11:14:01 +00:00
|
|
|
func (p *Protocol) checkForNetworkInfos(dhcppacket layers.DHCPv4) {
|
2019-12-03 16:47:38 +00:00
|
|
|
// Check if it is a DHCP request
|
|
|
|
if dhcppacket.Operation == layers.DHCPOpRequest {
|
|
|
|
// We can ignore requests, they won't help us here
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for different options (1, 3, 6, 28, 42, 51, 58) in DHCP Packet Options
|
|
|
|
for _, o := range dhcppacket.Options {
|
|
|
|
if isRelevantOption(o) {
|
|
|
|
// Found DHCP option to be watched, let's watch it
|
2019-12-09 11:14:01 +00:00
|
|
|
p.saveOption(o)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saves the given option in the networkSetup map, and informs the user if the value changes
|
|
|
|
func (p *Protocol) saveOption(opt layers.DHCPOption) {
|
|
|
|
// check if we already stored this value
|
|
|
|
if p.networkSetup[opt.Type] != nil {
|
|
|
|
// We already stored a value, let's check if it's the same as the new one
|
|
|
|
if !bytes.Equal(p.networkSetup[opt.Type], opt.Data) {
|
|
|
|
// Already stored a value and it's different from our new value - inform user and overwrite value later
|
|
|
|
log.Printf("Received different values for DHCP Option %s (ID %d). (Old: %s, New. %s)", opt.Type.String(), opt.Type, p.networkSetup[opt.Type], opt.Data)
|
|
|
|
} else {
|
|
|
|
// Exactly this value was already stored, no need to overwrite it
|
|
|
|
return
|
2019-12-03 16:47:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 11:14:01 +00:00
|
|
|
p.networkSetup[opt.Type] = opt.Data
|
2019-12-03 16:47:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Checks if the given DHCPOption is part of the watchlist
|
|
|
|
func isRelevantOption(opt layers.DHCPOption) bool {
|
|
|
|
// Iterate over all DHCP options in our watchlist
|
|
|
|
for _, o := range watchedOpts {
|
|
|
|
if o == opt.Type {
|
|
|
|
// Found.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This option is not on our watchlist.
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Formats the given byte array as string representing the IP address, or returns an error (as string)
|
2020-12-14 13:27:49 +00:00
|
|
|
// The returned bool value states whether the IP address could be formatted or not (e.g. "not found")
|
|
|
|
func formatIP(rawIP []byte) (string, bool) {
|
2019-12-03 16:47:38 +00:00
|
|
|
// Check if we even have an IP
|
|
|
|
if rawIP == nil {
|
|
|
|
// We don't have an IP, construct an error message (as string)
|
|
|
|
error := color.New(color.FgRed)
|
2020-12-14 13:27:49 +00:00
|
|
|
return error.Sprint("(not found)"), false
|
2019-12-03 16:47:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return formatted IP
|
2020-12-14 13:27:49 +00:00
|
|
|
return net.IP(rawIP).String(), true
|
2019-12-03 16:47:38 +00:00
|
|
|
}
|
|
|
|
|
2020-12-14 13:27:49 +00:00
|
|
|
// Formats the given byte array as string representing the date, or returns an error (as string)
|
|
|
|
// The returned bool value states whether the date could be formatted or not (e.g. "not found")
|
|
|
|
func formatDate(rawDate []byte) (string, bool) {
|
2019-12-03 16:47:38 +00:00
|
|
|
// Check if we even have a date
|
|
|
|
if rawDate == nil {
|
|
|
|
// We don't have a date, construct an error message (as string)
|
|
|
|
error := color.New(color.FgRed)
|
2020-12-14 13:27:49 +00:00
|
|
|
return error.Sprint("(not found)"), false
|
2019-12-03 16:47:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Actually format date
|
|
|
|
intDate := binary.LittleEndian.Uint32(rawDate)
|
|
|
|
seconds := intDate % 60
|
|
|
|
minutes := intDate / 60 % 60
|
|
|
|
hours := intDate / 60 / 60 % 60
|
|
|
|
formattedDate := ""
|
|
|
|
|
|
|
|
// Check which words we need to pick
|
|
|
|
// ... regarding hours
|
|
|
|
if hours > 0 {
|
|
|
|
formattedDate = fmt.Sprintf("%d hours", hours)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ... regarding minutes
|
|
|
|
if minutes > 0 {
|
|
|
|
// check if we got a previous string we need to take care of
|
|
|
|
if len(formattedDate) > 0 {
|
|
|
|
// yes, append our information to existing string
|
|
|
|
formattedDate = fmt.Sprintf("%s, %d minutes", formattedDate, minutes)
|
|
|
|
} else {
|
|
|
|
// no, use our string
|
|
|
|
formattedDate = fmt.Sprintf("%d minutes", minutes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ... regarding seconds
|
|
|
|
if seconds > 0 {
|
|
|
|
// check if we got a previous string we need to take care of
|
|
|
|
if len(formattedDate) > 0 {
|
|
|
|
// yes, append our information to existing string
|
|
|
|
formattedDate = fmt.Sprintf("%s, %d seconds", formattedDate, seconds)
|
|
|
|
} else {
|
|
|
|
// no, use our string
|
|
|
|
formattedDate = fmt.Sprintf("%d seconds", seconds)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-14 13:27:49 +00:00
|
|
|
return formattedDate, true
|
2019-12-03 16:47:38 +00:00
|
|
|
}
|