blob: 7e7f361bd5a55172e0007ac4f86589aea9e990ed [file] [log] [blame]
Matteo Scandolo90d08f62020-10-29 12:06:55 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dhcp
18
19import (
20 "encoding/hex"
21 "errors"
22 "fmt"
23 "github.com/google/gopacket"
24 "github.com/google/gopacket/layers"
25 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
26 log "github.com/sirupsen/logrus"
27 "net"
28)
29
30type DHCPServerIf interface {
31 HandleServerPacket(pkt gopacket.Packet) (gopacket.Packet, error)
32}
33
34type DHCPServer struct {
35 DHCPServerMacAddress net.HardwareAddr
36}
37
38func NewDHCPServer() *DHCPServer {
39 return &DHCPServer{
40 // NOTE we may need to make this configurable in case we'll need multiple servers
41 DHCPServerMacAddress: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
42 }
43}
44
45func (s *DHCPServer) getClientMacAddress(pkt gopacket.Packet) (net.HardwareAddr, error) {
46 dhcpLayer, err := GetDhcpLayer(pkt)
47 if err != nil {
48 return nil, err
49 }
50 return dhcpLayer.ClientHWAddr, nil
51}
52
53func (s *DHCPServer) getTxId(pkt gopacket.Packet) (uint32, error) {
54 dhcpLayer, err := GetDhcpLayer(pkt)
55 if err != nil {
56 return 0, err
57 }
58 return dhcpLayer.Xid, nil
59}
60
61func (s *DHCPServer) getPacketHostName(pkt gopacket.Packet) ([]byte, error) {
62 dhcpLayer, err := GetDhcpLayer(pkt)
63 if err != nil {
64 return nil, err
65 }
66 for _, option := range dhcpLayer.Options {
67 if option.Type == layers.DHCPOptHostname {
68 return option.Data, nil
69 }
70 }
71 return nil, errors.New("hostname-not-found")
72}
73
74func (s *DHCPServer) getOption82(pkt gopacket.Packet) ([]byte, error) {
75 dhcpLayer, err := GetDhcpLayer(pkt)
76 if err != nil {
77 return nil, err
78 }
79 for _, option := range dhcpLayer.Options {
80 if option.Type == 82 {
81 return option.Data, nil
82 }
83 }
84 log.WithFields(log.Fields{
85 "pkt": hex.EncodeToString(pkt.Data()),
86 }).Debug("option82-not-found")
87 return []byte{}, nil
88}
89
90func (s *DHCPServer) createDefaultDhcpReply(xid uint32, mac net.HardwareAddr) layers.DHCPv4 {
91 clientIp := s.createIpFromMacAddress(mac)
92 return layers.DHCPv4{
93 Operation: layers.DHCPOpReply,
94 HardwareType: layers.LinkTypeEthernet,
95 HardwareLen: 6,
96 HardwareOpts: 0,
97 Xid: xid,
98 ClientHWAddr: mac,
99 ClientIP: clientIp,
100 YourClientIP: clientIp,
101 }
102}
103
104func (s *DHCPServer) createIpFromMacAddress(mac net.HardwareAddr) net.IP {
105 ip := []byte{}
106 for i := 2; i < 6; i++ {
107 ip = append(ip, mac[i])
108 }
109 return net.IPv4(10+ip[0], ip[1], ip[2], ip[3])
110}
111
112func (s *DHCPServer) serializeServerDHCPPacket(clientMac net.HardwareAddr, dhcpLayer *layers.DHCPv4) (gopacket.Packet, error) {
113 buffer := gopacket.NewSerializeBuffer()
114
115 options := gopacket.SerializeOptions{
116 ComputeChecksums: true,
117 FixLengths: true,
118 }
119
120 ethernetLayer := &layers.Ethernet{
121 SrcMAC: s.DHCPServerMacAddress,
122 DstMAC: clientMac,
123 EthernetType: layers.EthernetTypeIPv4,
124 }
125
126 ipLayer := &layers.IPv4{
127 Version: 4,
128 TOS: 0x10,
129 TTL: 128,
130 SrcIP: []byte{0, 0, 0, 0},
131 DstIP: []byte{255, 255, 255, 255},
132 Protocol: layers.IPProtocolUDP,
133 }
134
135 udpLayer := &layers.UDP{
136 SrcPort: 67,
137 DstPort: 68,
138 }
139
140 _ = udpLayer.SetNetworkLayerForChecksum(ipLayer)
141 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcpLayer); err != nil {
142 dhcpLogger.Error("SerializeLayers")
143 return nil, err
144 }
145
146 return gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeEthernet, gopacket.Default), nil
147
148}
149
150func (s *DHCPServer) getDefaultDhcpServerOptions(hostname []byte, option82 []byte) []layers.DHCPOption {
151 defaultOpts := []layers.DHCPOption{}
152 defaultOpts = append(defaultOpts, layers.DHCPOption{
153 Type: layers.DHCPOptHostname,
154 Data: hostname,
155 Length: uint8(len(hostname)),
156 })
157
158 defaultOpts = append(defaultOpts, layers.DHCPOption{
159 Type: 82,
160 Data: option82,
161 Length: uint8(len(option82)),
162 })
163
164 return defaultOpts
165}
166
167// get a Discover packet an return a valid Offer
168func (s *DHCPServer) handleDiscover(pkt gopacket.Packet) (gopacket.Packet, error) {
169
170 sTag, cTag, err := packetHandlers.GetTagsFromPacket(pkt)
171 if err != nil {
172 return nil, err
173 }
174
175 clientMac, err := s.getClientMacAddress(pkt)
176 if err != nil {
177 return nil, err
178 }
179
180 txId, err := s.getTxId(pkt)
181 if err != nil {
182 return nil, err
183 }
184
185 hostname, err := s.getPacketHostName(pkt)
186 if err != nil {
187 return nil, err
188 }
189
190 option82, err := s.getOption82(pkt)
191 if err != nil {
192 return nil, err
193 }
194
195 dhcpLogger.WithFields(log.Fields{
196 "sTag": sTag,
197 "cTag": cTag,
198 "clientMac": clientMac,
199 "txId": txId,
200 "hostname": string(hostname),
201 "option82": string(option82),
202 }).Debug("Handling DHCP Discovery packet")
203
204 dhcpLayer := s.createDefaultDhcpReply(txId, clientMac)
205 defaultOpts := s.getDefaultDhcpServerOptions(hostname, option82)
206
207 dhcpLayer.Options = append([]layers.DHCPOption{{
208 Type: layers.DHCPOptMessageType,
209 Data: []byte{byte(layers.DHCPMsgTypeOffer)},
210 Length: 1,
211 }}, defaultOpts...)
212
213 data := []byte{01}
214 data = append(data, clientMac...)
215 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
216 Type: layers.DHCPOptClientID,
217 Data: data,
218 Length: uint8(len(data)),
219 })
220
221 // serialize the packet
222 responsePkt, err := s.serializeServerDHCPPacket(clientMac, &dhcpLayer)
223 if err != nil {
224 return nil, err
225 }
226
227 taggedResponsePkt, err := packetHandlers.PushDoubleTag(int(sTag), int(cTag), responsePkt, 0)
228 if err != nil {
229 return nil, err
230 }
231 return taggedResponsePkt, nil
232}
233
234func (s *DHCPServer) handleRequest(pkt gopacket.Packet) (gopacket.Packet, error) {
235 sTag, cTag, err := packetHandlers.GetTagsFromPacket(pkt)
236 if err != nil {
237 return nil, err
238 }
239
240 clientMac, err := s.getClientMacAddress(pkt)
241 if err != nil {
242 return nil, err
243 }
244
245 txId, err := s.getTxId(pkt)
246 if err != nil {
247 return nil, err
248 }
249
250 hostname, err := s.getPacketHostName(pkt)
251 if err != nil {
252 return nil, err
253 }
254
255 option82, err := s.getOption82(pkt)
256 if err != nil {
257 return nil, err
258 }
259
260 dhcpLogger.WithFields(log.Fields{
261 "sTag": sTag,
262 "cTag": cTag,
263 "clientMac": clientMac,
264 "txId": txId,
265 "hostname": string(hostname),
266 "option82": string(option82),
267 }).Debug("Handling DHCP Request packet")
268
269 dhcpLayer := s.createDefaultDhcpReply(txId, clientMac)
270 defaultOpts := s.getDefaultDhcpServerOptions(hostname, option82)
271
272 dhcpLayer.Options = append([]layers.DHCPOption{{
273 Type: layers.DHCPOptMessageType,
274 Data: []byte{byte(layers.DHCPMsgTypeAck)},
275 Length: 1,
276 }}, defaultOpts...)
277
278 // TODO can we move this in getDefaultDhcpServerOptions?
279 data := []byte{01}
280 data = append(data, clientMac...)
281 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
282 Type: layers.DHCPOptClientID,
283 Data: data,
284 Length: uint8(len(data)),
285 })
286
287 // serialize the packet
288 responsePkt, err := s.serializeServerDHCPPacket(clientMac, &dhcpLayer)
289 if err != nil {
290 return nil, err
291 }
292
293 taggedResponsePkt, err := packetHandlers.PushDoubleTag(int(sTag), int(cTag), responsePkt, 0)
294 if err != nil {
295 return nil, err
296 }
297 return taggedResponsePkt, nil
298}
299
300// HandleServerPacket is a very simple implementation of a DHCP server
301// that only replies to DHCPDiscover and DHCPRequest packets
302func (s DHCPServer) HandleServerPacket(pkt gopacket.Packet) (gopacket.Packet, error) {
303 dhcpLayer, _ := GetDhcpLayer(pkt)
304
305 if dhcpLayer.Operation == layers.DHCPOpReply {
306 dhcpLogger.WithFields(log.Fields{
307 "pkt": hex.EncodeToString(pkt.Data()),
308 }).Error("Received DHCP Reply on the server. Ignoring the packet but this is a serious error.")
309 }
310
311 dhcpMessageType, _ := GetDhcpMessageType(dhcpLayer)
312
313 switch dhcpMessageType {
314 case layers.DHCPMsgTypeDiscover:
315 dhcpLogger.Info("Received DHCP Discover")
316 return s.handleDiscover(pkt)
317 case layers.DHCPMsgTypeRequest:
318 dhcpLogger.Info("Received DHCP Request")
319 return s.handleRequest(pkt)
320 }
321 return nil, fmt.Errorf("cannot-handle-dhcp-packet-of-type-%s", dhcpMessageType.String())
322}