// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package dhcpv4 import ( "encoding/binary" "encoding/hex" "fmt" "net" "strings" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/elastic/beats/libbeat/common" ) func optionsToMap(options []dhcpv4.Option) (common.MapStr, error) { opts := common.MapStr{} for _, opt := range options { if opt.Code() == dhcpv4.OptionEnd { break } switch v := opt.(type) { case *dhcpv4.OptMessageType: mt, found := dhcpv4.MessageTypeToString[v.MessageType] if !found { mt = fmt.Sprintf("unknown (%v)", v.MessageType) } opts.Put("message_type", strings.ToLower(mt)) case *dhcpv4.OptParameterRequestList: var optNames []string for _, ro := range v.RequestedOpts { if name, ok := dhcpv4.OptionCodeToString[ro]; ok { optNames = append(optNames, name) } else { optNames = append(optNames, fmt.Sprintf("Unknown (%v)", ro)) } } opts.Put("parameter_request_list", optNames) case *dhcpv4.OptRequestedIPAddress: opts.Put("requested_ip_address", v.RequestedAddr.String()) case *dhcpv4.OptServerIdentifier: opts.Put("server_identifier", v.ServerID.String()) case *dhcpv4.OptBroadcastAddress: opts.Put("broadcast_address", v.BroadcastAddress.String()) case *dhcpv4.OptMaximumDHCPMessageSize: opts.Put("max_dhcp_message_size", v.Size) case *dhcpv4.OptClassIdentifier: opts.Put("class_identifier", v.Identifier) case *dhcpv4.OptDomainName: opts.Put("domain_name", v.DomainName) case *dhcpv4.OptDomainNameServer: var dnsServers []string for _, s := range v.NameServers { dnsServers = append(dnsServers, s.String()) } opts.Put("dns_servers", dnsServers) case *dhcpv4.OptVIVC: var subOptions []common.MapStr for _, vendorOpt := range v.Identifiers { subOptions = append(subOptions, common.MapStr{ "id": vendorOpt.EntID, "data": hex.EncodeToString(vendorOpt.Data), }) } opts.Put("vendor_identifying_options", subOptions) case *dhcpv4.OptionGeneric: // Generic options have just a []byte so we need to do extra parsing. switch opt.Code() { case dhcpv4.OptionSubnetMask: if len(v.Data) >= 4 { opts.Put("subnet_mask", net.IP(v.Data).String()) } case dhcpv4.OptionTimeOffset: if len(v.Data) >= 4 { opts.Put("utc_time_offset_sec", int32(binary.BigEndian.Uint32(v.Data))) } case dhcpv4.OptionRouter: ipOpt, err := ParseIPAddressOption(opt.ToBytes()) if err != nil { return nil, err } opts.Put("router", ipOpt.IPAddress.String()) case dhcpv4.OptionTimeServer: tsOpt, err := ParseIPAddressesOption(opt.ToBytes()) if err != nil { return nil, err } var timeServers []string for _, s := range tsOpt.IPAddresses { timeServers = append(timeServers, s.String()) } opts.Put("time_servers", timeServers) case dhcpv4.OptionNTPServers: tsOpt, err := ParseIPAddressesOption(opt.ToBytes()) if err != nil { return nil, err } var timeServers []string for _, s := range tsOpt.IPAddresses { timeServers = append(timeServers, s.String()) } opts.Put("ntp_servers", timeServers) case dhcpv4.OptionHostName: txt, err := ParseTextOption(opt.ToBytes()) if err != nil { return nil, err } opts.Put("hostname", txt.Text) case dhcpv4.OptionIPAddressLeaseTime: if len(v.Data) >= 4 { opts.Put("ip_address_lease_time_sec", binary.BigEndian.Uint32(v.Data)) } case dhcpv4.OptionMessage: txt, err := ParseTextOption(opt.ToBytes()) if err != nil { return nil, err } opts.Put("message", txt.Text) case dhcpv4.OptionRenewTimeValue: if len(v.Data) >= 4 { opts.Put("renewal_time_sec", binary.BigEndian.Uint32(v.Data)) } case dhcpv4.OptionRebindingTimeValue: if len(v.Data) >= 4 { opts.Put("rebinding_time_sec", binary.BigEndian.Uint32(v.Data)) } case dhcpv4.OptionBootfileName: txt, err := ParseTextOption(opt.ToBytes()) if err != nil { return nil, err } opts.Put("boot_file_name", txt.Text) } } } if len(opts) > 0 { return opts, nil } return nil, nil }