blob: 9ac8f423baeefa6f2d3be539081633f8ef431b04 [file] [log] [blame]
Matteo Scandolo90d08f62020-10-29 12:06:55 -07001/*
Joey Armstrong2c039362024-02-04 18:51:52 -05002 * Copyright 2018-2024 Open Networking Foundation (ONF) and the ONF Contributors
Matteo Scandolo90d08f62020-10-29 12:06:55 -07003
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"
Elia Battistonbed2d212022-02-02 16:23:31 +010023 "net"
24
Matteo Scandolo90d08f62020-10-29 12:06:55 -070025 "github.com/google/gopacket"
26 "github.com/google/gopacket/layers"
27 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
28 log "github.com/sirupsen/logrus"
Matteo Scandolo90d08f62020-10-29 12:06:55 -070029)
30
31type DHCPServerIf interface {
32 HandleServerPacket(pkt gopacket.Packet) (gopacket.Packet, error)
33}
34
35type DHCPServer struct {
36 DHCPServerMacAddress net.HardwareAddr
37}
38
39func NewDHCPServer() *DHCPServer {
40 return &DHCPServer{
41 // NOTE we may need to make this configurable in case we'll need multiple servers
42 DHCPServerMacAddress: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
43 }
44}
45
46func (s *DHCPServer) getClientMacAddress(pkt gopacket.Packet) (net.HardwareAddr, error) {
47 dhcpLayer, err := GetDhcpLayer(pkt)
48 if err != nil {
49 return nil, err
50 }
51 return dhcpLayer.ClientHWAddr, nil
52}
53
54func (s *DHCPServer) getTxId(pkt gopacket.Packet) (uint32, error) {
55 dhcpLayer, err := GetDhcpLayer(pkt)
56 if err != nil {
57 return 0, err
58 }
59 return dhcpLayer.Xid, nil
60}
61
62func (s *DHCPServer) getPacketHostName(pkt gopacket.Packet) ([]byte, error) {
63 dhcpLayer, err := GetDhcpLayer(pkt)
64 if err != nil {
65 return nil, err
66 }
67 for _, option := range dhcpLayer.Options {
68 if option.Type == layers.DHCPOptHostname {
69 return option.Data, nil
70 }
71 }
72 return nil, errors.New("hostname-not-found")
73}
74
75func (s *DHCPServer) getOption82(pkt gopacket.Packet) ([]byte, error) {
76 dhcpLayer, err := GetDhcpLayer(pkt)
77 if err != nil {
78 return nil, err
79 }
80 for _, option := range dhcpLayer.Options {
81 if option.Type == 82 {
82 return option.Data, nil
83 }
84 }
85 log.WithFields(log.Fields{
86 "pkt": hex.EncodeToString(pkt.Data()),
87 }).Debug("option82-not-found")
88 return []byte{}, nil
89}
90
91func (s *DHCPServer) createDefaultDhcpReply(xid uint32, mac net.HardwareAddr) layers.DHCPv4 {
92 clientIp := s.createIpFromMacAddress(mac)
93 return layers.DHCPv4{
94 Operation: layers.DHCPOpReply,
95 HardwareType: layers.LinkTypeEthernet,
96 HardwareLen: 6,
97 HardwareOpts: 0,
98 Xid: xid,
99 ClientHWAddr: mac,
100 ClientIP: clientIp,
101 YourClientIP: clientIp,
102 }
103}
104
105func (s *DHCPServer) createIpFromMacAddress(mac net.HardwareAddr) net.IP {
106 ip := []byte{}
107 for i := 2; i < 6; i++ {
108 ip = append(ip, mac[i])
109 }
110 return net.IPv4(10+ip[0], ip[1], ip[2], ip[3])
111}
112
113func (s *DHCPServer) serializeServerDHCPPacket(clientMac net.HardwareAddr, dhcpLayer *layers.DHCPv4) (gopacket.Packet, error) {
114 buffer := gopacket.NewSerializeBuffer()
115
116 options := gopacket.SerializeOptions{
117 ComputeChecksums: true,
118 FixLengths: true,
119 }
120
121 ethernetLayer := &layers.Ethernet{
122 SrcMAC: s.DHCPServerMacAddress,
123 DstMAC: clientMac,
124 EthernetType: layers.EthernetTypeIPv4,
125 }
126
127 ipLayer := &layers.IPv4{
128 Version: 4,
129 TOS: 0x10,
130 TTL: 128,
131 SrcIP: []byte{0, 0, 0, 0},
132 DstIP: []byte{255, 255, 255, 255},
133 Protocol: layers.IPProtocolUDP,
134 }
135
136 udpLayer := &layers.UDP{
137 SrcPort: 67,
138 DstPort: 68,
139 }
140
141 _ = udpLayer.SetNetworkLayerForChecksum(ipLayer)
142 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcpLayer); err != nil {
143 dhcpLogger.Error("SerializeLayers")
144 return nil, err
145 }
146
147 return gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeEthernet, gopacket.Default), nil
148
149}
150
151func (s *DHCPServer) getDefaultDhcpServerOptions(hostname []byte, option82 []byte) []layers.DHCPOption {
152 defaultOpts := []layers.DHCPOption{}
153 defaultOpts = append(defaultOpts, layers.DHCPOption{
154 Type: layers.DHCPOptHostname,
155 Data: hostname,
156 Length: uint8(len(hostname)),
157 })
158
159 defaultOpts = append(defaultOpts, layers.DHCPOption{
160 Type: 82,
161 Data: option82,
162 Length: uint8(len(option82)),
163 })
164
165 return defaultOpts
166}
167
168// get a Discover packet an return a valid Offer
169func (s *DHCPServer) handleDiscover(pkt gopacket.Packet) (gopacket.Packet, error) {
170
Elia Battistonbed2d212022-02-02 16:23:31 +0100171 oTag, iTag, err := packetHandlers.GetTagsFromPacket(pkt)
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700172 if err != nil {
173 return nil, err
174 }
175
176 clientMac, err := s.getClientMacAddress(pkt)
177 if err != nil {
178 return nil, err
179 }
180
181 txId, err := s.getTxId(pkt)
182 if err != nil {
183 return nil, err
184 }
185
186 hostname, err := s.getPacketHostName(pkt)
187 if err != nil {
188 return nil, err
189 }
190
191 option82, err := s.getOption82(pkt)
192 if err != nil {
193 return nil, err
194 }
195
196 dhcpLogger.WithFields(log.Fields{
Elia Battistonbed2d212022-02-02 16:23:31 +0100197 "oTag": oTag,
198 "iTag": iTag,
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700199 "clientMac": clientMac,
200 "txId": txId,
201 "hostname": string(hostname),
202 "option82": string(option82),
203 }).Debug("Handling DHCP Discovery packet")
204
205 dhcpLayer := s.createDefaultDhcpReply(txId, clientMac)
206 defaultOpts := s.getDefaultDhcpServerOptions(hostname, option82)
207
208 dhcpLayer.Options = append([]layers.DHCPOption{{
209 Type: layers.DHCPOptMessageType,
210 Data: []byte{byte(layers.DHCPMsgTypeOffer)},
211 Length: 1,
212 }}, defaultOpts...)
213
214 data := []byte{01}
215 data = append(data, clientMac...)
216 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
217 Type: layers.DHCPOptClientID,
218 Data: data,
219 Length: uint8(len(data)),
220 })
221
222 // serialize the packet
223 responsePkt, err := s.serializeServerDHCPPacket(clientMac, &dhcpLayer)
224 if err != nil {
225 return nil, err
226 }
227
Elia Battistonbed2d212022-02-02 16:23:31 +0100228 var taggedResponsePkt gopacket.Packet
229 if iTag != 0 { //Double tagged
230 taggedResponsePkt, err = packetHandlers.PushDoubleTag(int(oTag), int(iTag), responsePkt, 0)
231 } else { //Single tagged
232 taggedResponsePkt, err = packetHandlers.PushSingleTag(int(oTag), responsePkt, 0)
233 }
234
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700235 if err != nil {
236 return nil, err
237 }
Elia Battistonbed2d212022-02-02 16:23:31 +0100238
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700239 return taggedResponsePkt, nil
240}
241
242func (s *DHCPServer) handleRequest(pkt gopacket.Packet) (gopacket.Packet, error) {
Elia Battistonbed2d212022-02-02 16:23:31 +0100243 oTag, iTag, err := packetHandlers.GetTagsFromPacket(pkt)
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700244 if err != nil {
245 return nil, err
246 }
247
248 clientMac, err := s.getClientMacAddress(pkt)
249 if err != nil {
250 return nil, err
251 }
252
253 txId, err := s.getTxId(pkt)
254 if err != nil {
255 return nil, err
256 }
257
258 hostname, err := s.getPacketHostName(pkt)
259 if err != nil {
260 return nil, err
261 }
262
263 option82, err := s.getOption82(pkt)
264 if err != nil {
265 return nil, err
266 }
267
268 dhcpLogger.WithFields(log.Fields{
Elia Battistonbed2d212022-02-02 16:23:31 +0100269 "oTag": oTag,
270 "iTag": iTag,
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700271 "clientMac": clientMac,
272 "txId": txId,
273 "hostname": string(hostname),
274 "option82": string(option82),
275 }).Debug("Handling DHCP Request packet")
276
277 dhcpLayer := s.createDefaultDhcpReply(txId, clientMac)
278 defaultOpts := s.getDefaultDhcpServerOptions(hostname, option82)
279
280 dhcpLayer.Options = append([]layers.DHCPOption{{
281 Type: layers.DHCPOptMessageType,
282 Data: []byte{byte(layers.DHCPMsgTypeAck)},
283 Length: 1,
284 }}, defaultOpts...)
285
286 // TODO can we move this in getDefaultDhcpServerOptions?
287 data := []byte{01}
288 data = append(data, clientMac...)
289 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
290 Type: layers.DHCPOptClientID,
291 Data: data,
292 Length: uint8(len(data)),
293 })
294
295 // serialize the packet
296 responsePkt, err := s.serializeServerDHCPPacket(clientMac, &dhcpLayer)
297 if err != nil {
298 return nil, err
299 }
300
Elia Battistonbed2d212022-02-02 16:23:31 +0100301 var taggedResponsePkt gopacket.Packet
302 if iTag != 0 { //Double tagged
303 taggedResponsePkt, err = packetHandlers.PushDoubleTag(int(oTag), int(iTag), responsePkt, 0)
304 } else { //Single tagged
305 taggedResponsePkt, err = packetHandlers.PushSingleTag(int(oTag), responsePkt, 0)
306 }
307
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700308 if err != nil {
309 return nil, err
310 }
Elia Battistonbed2d212022-02-02 16:23:31 +0100311
Matteo Scandolo90d08f62020-10-29 12:06:55 -0700312 return taggedResponsePkt, nil
313}
314
315// HandleServerPacket is a very simple implementation of a DHCP server
316// that only replies to DHCPDiscover and DHCPRequest packets
317func (s DHCPServer) HandleServerPacket(pkt gopacket.Packet) (gopacket.Packet, error) {
318 dhcpLayer, _ := GetDhcpLayer(pkt)
319
320 if dhcpLayer.Operation == layers.DHCPOpReply {
321 dhcpLogger.WithFields(log.Fields{
322 "pkt": hex.EncodeToString(pkt.Data()),
323 }).Error("Received DHCP Reply on the server. Ignoring the packet but this is a serious error.")
324 }
325
326 dhcpMessageType, _ := GetDhcpMessageType(dhcpLayer)
327
328 switch dhcpMessageType {
329 case layers.DHCPMsgTypeDiscover:
330 dhcpLogger.Info("Received DHCP Discover")
331 return s.handleDiscover(pkt)
332 case layers.DHCPMsgTypeRequest:
333 dhcpLogger.Info("Received DHCP Request")
334 return s.handleRequest(pkt)
335 }
336 return nil, fmt.Errorf("cannot-handle-dhcp-packet-of-type-%s", dhcpMessageType.String())
337}