blob: b779f1f3ccb5f9e957bb381fe7685c2d7d0a8a54 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301/*
2* Copyright 2022-present Open Networking Foundation
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14 */
15
16package application
17
18import (
19 "encoding/hex"
Tinoj Joseph07cc5372022-07-18 22:53:51 +053020 "context"
Naveen Sampath04696f72022-06-13 15:19:14 +053021 "errors"
22 "net"
23 "sync"
24
25 "github.com/google/gopacket"
26 "github.com/google/gopacket/layers"
27
28 cntlr "voltha-go-controller/internal/pkg/controller"
29 "voltha-go-controller/internal/pkg/of"
30 "voltha-go-controller/internal/pkg/util"
Tinoj Joseph1d108322022-07-13 10:07:39 +053031 "voltha-go-controller/log"
Naveen Sampath04696f72022-06-13 15:19:14 +053032)
33
34// DhcpRelayState type
35type DhcpRelayState uint8
36
37const (
38 // DhcpRelayStateNone constant
39 DhcpRelayStateNone DhcpRelayState = iota
40 // DhcpRelayStateDiscover constant
41 DhcpRelayStateDiscover
42 // DhcpRelayStateOffer constant
43 DhcpRelayStateOffer
44 // DhcpRelayStateRequest constant
45 DhcpRelayStateRequest
46 // DhcpRelayStateAck constant
47 DhcpRelayStateAck
48 // DhcpRelayStateNAK constant
49 DhcpRelayStateNAK
50 // DhcpRelayStateRelease constant
51 DhcpRelayStateRelease
52)
53
54// RemoteIDType represents data type for various RemoteID types
55type RemoteIDType string
56
57// List of RemoteID types supported
58const (
59 MACAddress RemoteIDType = "MAC_ADDRESS"
60 CustomRemotedID RemoteIDType = "Custom"
61)
62
63// MaxLenDhcpv6DUID constant
64const MaxLenDhcpv6DUID = 130 // 2: DUID-Type, 128: MaxLen of DUID value
65
66// opt82 constant
67const opt82 = 82
68
69// Dhcpv6RelayState type
70type Dhcpv6RelayState uint8
71
72const (
73 // Dhcpv6RelayStateNone constant
74 Dhcpv6RelayStateNone Dhcpv6RelayState = iota
75 // Dhcpv6RelayStateSolicit constant
76 Dhcpv6RelayStateSolicit
77 // Dhcpv6RelayStateReply constant
78 Dhcpv6RelayStateReply
79 // Dhcpv6RelayStateRelease constant
80 Dhcpv6RelayStateRelease
81)
82
83var (
84 // ErrSessionDoNotExist error type
85 ErrSessionDoNotExist = errors.New("Session Doesn't Exist")
86)
87
88// IDhcpRelaySession to get dhcp session field value
89type IDhcpRelaySession interface {
90 GetCircuitID() []byte
91 GetRemoteID() []byte
92 GetNniVlans() (uint16, uint16)
93 GetDhcpState() DhcpRelayState
94 GetDhcpv6State() Dhcpv6RelayState
95 SetDhcpState(DhcpRelayState)
96 SetDhcpv6State(Dhcpv6RelayState)
Tinoj Joseph07cc5372022-07-18 22:53:51 +053097 SetMacAddr(context.Context, net.HardwareAddr)
98 DhcpResultInd(context.Context, *layers.DHCPv4)
99 Dhcpv6ResultInd(cntx context.Context, ipv6Addr net.IP, leaseTime uint32)
Naveen Sampath04696f72022-06-13 15:19:14 +0530100}
101
102// DhcpRelayVnet : The DHCP relay sessions are stored in a map to be retrieved from when
103// a response is received from the network. The map uses the VLANs and the
104// the MAC address as key to finding the service
105// DHCP Relay Virtual Network hosts a set of DHCP relay sessions that belong
106// to the network. It supports two VLANs as its identify. If a single VLAN or
107// no VLAN is to be used, those two should be passed as 4096 (VlanNone)
108type DhcpRelayVnet struct {
109 OuterVlan uint16
110 InnerVlan uint16
111 sessions map[[6]byte]IDhcpRelaySession
112 sessionsv6 map[[MaxLenDhcpv6DUID]byte]IDhcpRelaySession
113 sessionLock sync.RWMutex
114}
115
116// DhcpNetworks hosts different DHCP networks that in turn hold the DHCP
117// sessions
118type DhcpNetworks struct {
119 Networks map[uint32]*DhcpRelayVnet
120}
121
122func init() {
123 RegisterPacketHandler(DHCPv4, ProcessUDP4Packet)
124 RegisterPacketHandler(DHCPv6, ProcessUDP6Packet)
125}
126
127// NewDhcpRelayVnet is constructor for a DHCP Relay Virtual network
128func NewDhcpRelayVnet(outerVlan uint16, innerVlan uint16) *DhcpRelayVnet {
129 var drv DhcpRelayVnet
130
131 drv.OuterVlan = outerVlan
132 drv.InnerVlan = innerVlan
133 drv.sessions = make(map[[6]byte]IDhcpRelaySession)
134 drv.sessionsv6 = make(map[[MaxLenDhcpv6DUID]byte]IDhcpRelaySession)
135 return &drv
136}
137
138// GetDhcpVnet to add dhcp vnet
139func (dn *DhcpNetworks) GetDhcpVnet(outerVlan uint16, innerVlan uint16) *DhcpRelayVnet {
140 comboVlan := uint32(outerVlan)<<16 + uint32(innerVlan)
141 drv, ok := dn.Networks[comboVlan]
142 if ok {
143 return drv
144 }
145 return nil
146}
147
148// AddDhcpVnet to add dhcp vnet
149func (dn *DhcpNetworks) AddDhcpVnet(outerVlan uint16, innerVlan uint16) *DhcpRelayVnet {
150 comboVlan := uint32(outerVlan)<<16 + uint32(innerVlan)
151 if drv, ok := dn.Networks[comboVlan]; ok {
152 return drv
153 }
154 drv := NewDhcpRelayVnet(outerVlan, innerVlan)
155 dn.Networks[comboVlan] = drv
156 return drv
157}
158
159// NewDhcpNetworks to get new dhcp network
160func NewDhcpNetworks() *DhcpNetworks {
161 var dn DhcpNetworks
162 dn.Networks = make(map[uint32]*DhcpRelayVnet)
163 return &dn
164}
165
166// AddDhcpSession to add dhcp session
167func (dn *DhcpNetworks) AddDhcpSession(pkt gopacket.Packet, session IDhcpRelaySession) error {
168 var key [6]byte
169 ethl := pkt.Layer(layers.LayerTypeEthernet)
170 eth, _ := ethl.(*layers.Ethernet)
171 addr := eth.SrcMAC
172 if len(addr) != 6 {
173 logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
174 return errors.New("Invalid MAC address")
175 }
176 copy(key[:], addr[0:6])
177
178 drv := dn.AddDhcpVnet(session.GetNniVlans())
179
180 drv.sessionLock.Lock()
181 drv.sessions[key] = session
182 drv.sessionLock.Unlock()
183 return nil
184}
185
186// DelDhcpSession to delete dhcp session
187func (dn *DhcpNetworks) DelDhcpSession(pkt gopacket.Packet, session IDhcpRelaySession) {
188 var key [6]byte
189 ethl := pkt.Layer(layers.LayerTypeEthernet)
190 eth, _ := ethl.(*layers.Ethernet)
191 addr := eth.SrcMAC
192 if len(addr) != 6 {
193 logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
194 return
195 }
196 copy(key[:], addr[0:6])
197 drv := dn.AddDhcpVnet(session.GetNniVlans())
198 drv.sessionLock.Lock()
199 delete(drv.sessions, key)
200 drv.sessionLock.Unlock()
201}
202
203// delDhcpSessions to delete dhcp sessions
204func delDhcpSessions(addr net.HardwareAddr, outervlan of.VlanType, innervlan of.VlanType, sessionKey [MaxLenDhcpv6DUID]byte) {
205 var key [6]byte
206 if addr == nil || !NonZeroMacAddress(addr) {
207 logger.Warnw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
208 return
209 }
210 copy(key[:], addr[0:6])
211 drv := dhcpNws.AddDhcpVnet(uint16(outervlan), uint16(innervlan))
212 drv.sessionLock.Lock()
213 delete(drv.sessions, key)
214 delete(drv.sessionsv6, sessionKey)
215 drv.sessionLock.Unlock()
216 logger.Infow(ctx, "DHCP Sessions deleted", log.Fields{"MAC": addr})
217}
218
219// AddDhcp6Session to add dhcpv6 session
220func (dn *DhcpNetworks) AddDhcp6Session(key [MaxLenDhcpv6DUID]byte, session IDhcpRelaySession) error {
221 outerVlan, innerVlan := session.GetNniVlans()
222 logger.Infow(ctx, "Adding Session", log.Fields{"outerVlan": outerVlan, "innerVlan": innerVlan, "Addr": key})
223 drv := dn.AddDhcpVnet(outerVlan, innerVlan)
224 drv.sessionLock.Lock()
225 drv.sessionsv6[key] = session
226 drv.sessionLock.Unlock()
227 return nil
228}
229
230// DelDhcp6Session to delete dhcpv6 session
231func (dn *DhcpNetworks) DelDhcp6Session(key [MaxLenDhcpv6DUID]byte, session IDhcpRelaySession) {
232 outerVlan, innerVlan := session.GetNniVlans()
233 logger.Infow(ctx, "Get Session", log.Fields{"OuterVLAN": outerVlan, "InnerVLAN": innerVlan, "Addr": key})
234 drv := dn.GetDhcpVnet(outerVlan, innerVlan)
235 drv.sessionLock.Lock()
236 delete(drv.sessionsv6, key)
237 drv.sessionLock.Unlock()
238}
239
240// GetDhcpSession to get dhcp session info
241func (dn *DhcpNetworks) GetDhcpSession(outerVlan uint16, innerVlan uint16, addr net.HardwareAddr) (IDhcpRelaySession, error) {
242 var key [6]byte
243 if len(addr) != 6 {
244 logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
245 return nil, errors.New("Invalid MAC address")
246 }
247 copy(key[:], addr[0:6])
248 drv := dn.AddDhcpVnet(outerVlan, innerVlan)
249 drv.sessionLock.RLock()
250 defer drv.sessionLock.RUnlock()
251 if session, ok := drv.sessions[key]; ok {
252 return session, nil
253 }
254 return nil, ErrSessionDoNotExist
255}
256
257// GetDhcp6Session to get Dhcp6Session
258func (dn *DhcpNetworks) GetDhcp6Session(outerVlan uint16, innerVlan uint16, key [MaxLenDhcpv6DUID]byte) (IDhcpRelaySession, error) {
259 logger.Infow(ctx, "Locating Session", log.Fields{"OuterVlan": outerVlan, "InnerVlan": innerVlan, "key": key})
260
261 drv := dn.AddDhcpVnet(outerVlan, innerVlan)
262 drv.sessionLock.RLock()
263 defer drv.sessionLock.RUnlock()
264 if session, ok := drv.sessionsv6[key]; ok {
265 return session, nil
266 }
267 return nil, ErrSessionDoNotExist
268}
269
270// GetVlansFromPacket to get vlans from the packet
271func GetVlansFromPacket(pkt gopacket.Packet) (innerVlan of.VlanType, outerVlan of.VlanType) {
272
273 vlans := GetVlans(pkt)
274 if len(vlans) == 1 {
275 outerVlan = vlans[0]
276 innerVlan = of.VlanNone
277 } else if len(vlans) == 0 {
278 innerVlan = of.VlanNone
279 outerVlan = of.VlanNone
280 } else {
281 innerVlan = vlans[1]
282 outerVlan = vlans[0]
283 }
284 return
285}
286
287// GetVnetForV4Nni to get vnet for v4 Nni
288func GetVnetForV4Nni(dhcp *layers.DHCPv4, cvlan of.VlanType, svlan of.VlanType, pbit uint8) ([]*VoltPortVnet, error) {
289 var err error
290 var session IDhcpRelaySession
291 var vpvList []*VoltPortVnet
292 logger.Infow(ctx, "Mac Obtained MAC: ", log.Fields{"Addr": dhcp.ClientHWAddr})
293 session, err = dhcpNws.GetDhcpSession(uint16(svlan), uint16(cvlan), dhcp.ClientHWAddr)
294
295 if session != nil {
296 vpv, ok := session.(*VoltPortVnet)
297 logger.Infow(ctx, "Session Exist: VPV found", log.Fields{"VPV": vpv})
298 if ok {
299 vpvList = append(vpvList, vpv)
300 return vpvList, nil
301 }
302 }
303
304 if err == ErrSessionDoNotExist {
305 //No DHCP Session found, find matching VPV to send the packet out
306 logger.Info(ctx, "Session Doesnt Exist: Finding matching VPV")
307 return GetApplication().GetVpvsForDsPkt(cvlan, svlan, dhcp.ClientHWAddr, pbit)
308 }
309 return nil, errors.New("The session retrieved of wrong type")
310}
311
312// GetVnetForV6Nni to get vnet for v6 Nni
313func GetVnetForV6Nni(dhcp *layers.DHCPv6, cvlan of.VlanType, svlan of.VlanType,
314 pbit uint8, clientMAC net.HardwareAddr) ([]*VoltPortVnet, net.HardwareAddr, error) {
315 var err error
316 var session IDhcpRelaySession
317 var vpvList []*VoltPortVnet
318
319 var sessionKey [MaxLenDhcpv6DUID]byte
320
321 clientDuid, decodedDuid := getDhcpv6ClientDUID(dhcp)
322 if clientDuid == nil || decodedDuid == nil {
323 copy(sessionKey[:], clientMAC)
324 } else {
325 copy(sessionKey[:], clientDuid[0:])
326 if decodedDuid.Type == layers.DHCPv6DUIDTypeLLT || decodedDuid.Type == layers.DHCPv6DUIDTypeLL {
327 clientMAC = decodedDuid.LinkLayerAddress
328 }
329 }
330 session, err = dhcpNws.GetDhcp6Session(uint16(svlan), uint16(cvlan), sessionKey)
331 if session != nil {
332 vpv, ok := session.(*VoltPortVnet)
333 logger.Infow(ctx, "Session Exist: VPV found", log.Fields{"VPV": vpv})
334 if ok {
335 vpvList = append(vpvList, vpv)
336 return vpvList, clientMAC, nil
337 }
338 }
339
340 if err == ErrSessionDoNotExist {
341 //No DHCP Session found, find matching VPV to send the packet out
342 logger.Info(ctx, "Session Doesnt Exist: Finding matching VPV")
343 vpvList, err := GetApplication().GetVpvsForDsPkt(cvlan, svlan, clientMAC, pbit)
344 return vpvList, clientMAC, err
345 }
346 return nil, clientMAC, errors.New("The session retrieved of wrong type")
347}
348
349/*
350// getDhcpv4ClientMacAddr to get mac address for dhcpv4 client
351func getDhcpv4ClientMacAddr(pkt gopacket.Packet) net.HardwareAddr {
352 dhcp := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
353 logger.Infow(ctx, "Mac Obtained v4: ", log.Fields{"Addr": dhcp.ClientHWAddr})
354 return dhcp.ClientHWAddr
355}
356
357// getDhcpv6ClientMacAddr to get mac address for dhcpv6 client
358func getDhcpv6ClientMacAddr(dhcpv6 *layers.DHCPv6) net.HardwareAddr {
359 var cID layers.DHCPv6Option
360 for _, option := range dhcpv6.Options {
361 if option.Code == layers.DHCPv6OptClientID {
362 cID = option
363 }
364 }
365 duid := &layers.DHCPv6DUID{}
366
367 //If cID is not found, DecodeFromBytes() returns error on empty cID
368 if err := duid.DecodeFromBytes(cID.Data); err == nil {
369 logger.Infow(ctx, "Mac Obtained v6: ", log.Fields{"Addr": duid.LinkLayerAddress, "Option": cID.String()})
370 return duid.LinkLayerAddress
371 }
372 return nil
373}*/
374
375// getDhcpv6ClientDUID to get Dhcpv6 client DUID
376func getDhcpv6ClientDUID(dhcpv6 *layers.DHCPv6) ([]byte, *layers.DHCPv6DUID) {
377
378 for _, option := range dhcpv6.Options {
379 logger.Debugw(ctx, "DHCPv6 Options", log.Fields{"option": option.Code})
380 if option.Code == layers.DHCPv6OptClientID {
381 duid := &layers.DHCPv6DUID{}
382 err := duid.DecodeFromBytes(option.Data)
383 if err == nil {
384 logger.Infow(ctx, "ClientIdentifier", log.Fields{"DUID": duid, "Option": option.String()})
385 duidLen := len(option.Data)
386 if duidLen > 130 {
387 duidLen = 130
388 }
389 return option.Data[0:duidLen], duid
390 }
391 logger.Errorw(ctx, "Client DUID decode failed", log.Fields{"error": err})
392 break
393 }
394 }
395 logger.Error(ctx, "Client DUID is not present in the packet")
396 return nil, nil
397}
398
399// AddDhcpv4Option82 : DHCPv4 packet operations
400// Addition of DHCP Option 82 which codes circuit-id and remote-id
401// into the packet. This happens as the request is relayed to the
402// DHCP servers on the NNI
403func AddDhcpv4Option82(svc *VoltService, rID []byte, dhcpv4 *layers.DHCPv4) {
404 //NOTE : both cID and rID should not be empty if this function is called
405 cID := svc.GetCircuitID()
406 var data []byte
407 if len(cID) != 0 {
408 data = append(data, 0x01)
409 data = append(data, byte(len(cID)))
410 data = append(data, cID...)
411 }
412 if len(rID) != 0 {
413 data = append(data, 0x02)
414 data = append(data, byte(len(rID)))
415 data = append(data, rID...)
416 }
417
418 if svc.isDataRateAttrPresent() {
419 minDrUs := util.Uint32ToByte(svc.MinDataRateUs)
420 data = append(data, TYPEMINDATAUS)
421 data = append(data, byte(len(minDrUs)))
422 data = append(data, minDrUs...)
423
424 minDrDs := util.Uint32ToByte(svc.MinDataRateDs)
425 data = append(data, TYPEMINDATADS)
426 data = append(data, byte(len(minDrDs)))
427 data = append(data, minDrDs...)
428
429 maxDrUs := util.Uint32ToByte(svc.MaxDataRateUs)
430 data = append(data, TYPEMAXDATAUS)
431 data = append(data, byte(len(maxDrUs)))
432 data = append(data, maxDrUs...)
433
434 maxDrDs := util.Uint32ToByte(svc.MaxDataRateDs)
435 data = append(data, TYPEMAXDATADS)
436 data = append(data, byte(len(maxDrDs)))
437 data = append(data, maxDrDs...)
438 }
439
440 option := layers.NewDHCPOption(82, data)
441 dhcpv4.Options = append(dhcpv4.Options, option)
442}
443
444// DelOption82 : Deletion of option 82 from the packet received on the NNI interface.
445// Once the packet is received, the option 82 is stripped off and the
446// packet is forwarded towards access
447func DelOption82(dhcpv4 *layers.DHCPv4) {
448 for index, option := range dhcpv4.Options {
449 if option.Type == opt82 {
450 dhcpv4.Options = append(dhcpv4.Options[0:index], dhcpv4.Options[index+1:]...)
451 return
452 }
453 }
454}
455
456// DhcpMsgType returns the DHCP message type from the packet
457func DhcpMsgType(dhcp *layers.DHCPv4) layers.DHCPMsgType {
458 for _, option := range dhcp.Options {
459 if option.Type == layers.DHCPOptMessageType {
460 return layers.DHCPMsgType(option.Data[0])
461 }
462 }
463 return layers.DHCPMsgTypeUnspecified
464}
465
466// GetIpv4Addr returns the IP address in the DHCP reply
467func GetIpv4Addr(dhcp *layers.DHCPv4) (net.IP, int64) {
468 var leaseTime uint32
469 for _, opt := range dhcp.Options {
470 if opt.Type == layers.DHCPOptLeaseTime {
471 leaseTime = GetIPv4LeaseTime(opt)
472 }
473 }
474 return dhcp.YourClientIP, int64(leaseTime)
475}
476
477//GetIPv4LeaseTime get ip lease time
478func GetIPv4LeaseTime(opt layers.DHCPOption) uint32 {
479 return uint32(opt.Data[0])<<24 | uint32(opt.Data[1])<<16 | uint32(opt.Data[2])<<8 | uint32(opt.Data[3])
480}
481
482// GetIpv6Addr returns the IPv6 address in the DHCPv6 reply
483func GetIpv6Addr(dhcp6 *layers.DHCPv6) (net.IP, uint32) {
484 var ipv6Addr net.IP
485 var leaseTime uint32
486
487 //Check for IANA allocation, if not present, then look for IAPD allocation
488 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply {
489 ipv6Addr, leaseTime = GetIANAAddress(dhcp6)
490 if ipv6Addr == nil {
491 ipv6Addr, leaseTime = GetIAPDAddress(dhcp6)
492 }
493 }
494 return ipv6Addr, leaseTime
495}
496
497// GetIANAAddress returns the IPv6 address in the DHCPv6 reply
498func GetIANAAddress(dhcp6 *layers.DHCPv6) (net.IP, uint32) {
499 var ipv6Addr net.IP
500 var leaseTime uint32
501 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply {
502 for _, o := range dhcp6.Options {
503 if o.Code == layers.DHCPv6OptIANA {
504
505 iana := &layers.DHCPv6IANA{}
506 err := iana.DecodeFromBytes(o.Data)
507 if err == nil {
508 ipv6Addr = iana.IA.IPv6Addr
509 leaseTime = iana.IA.ValidLifeTime
510 logger.Debugw(ctx, "IPv6 Allocated", log.Fields{"IANA IPv6": ipv6Addr})
511 return ipv6Addr, leaseTime
512 }
Tinoj Joseph1d108322022-07-13 10:07:39 +0530513 logger.Warnw(ctx, "Decode of IANA Failed", log.Fields{"Reason": err.Error()})
Naveen Sampath04696f72022-06-13 15:19:14 +0530514 break
515 }
516 }
517 }
518 return nil, 0
519}
520
521// GetIAPDAddress returns the IPv6 address in the DHCPv6 reply
522func GetIAPDAddress(dhcp6 *layers.DHCPv6) (net.IP, uint32) {
523 var ipv6Addr net.IP
524 var leaseTime uint32
525 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply {
526 for _, o := range dhcp6.Options {
527 if o.Code == layers.DHCPv6OptIAPD {
528
529 iapd := &layers.DHCPv6IAPD{}
530 if err := iapd.DecodeFromBytes(o.Data); err == nil {
531 ipv6Addr = iapd.PD.Prefix
532 leaseTime = iapd.PD.ValidLifeTime
533 logger.Debugw(ctx, "IPv6 Allocated", log.Fields{"IAPD IPv6": ipv6Addr})
534 break
535 } else {
Tinoj Joseph1d108322022-07-13 10:07:39 +0530536 logger.Warnw(ctx, "Decode of IAPD Failed", log.Fields{"Reason": err.Error()})
Naveen Sampath04696f72022-06-13 15:19:14 +0530537 break
538 }
539 }
540 }
541 }
542 return ipv6Addr, leaseTime
543}
544
545// ProcessDsDhcpv4Packet : DHCPv4 packet processor functions
546// This function processes DS DHCP packet received on the NNI port.
547// The services are attached to the access ports. Thus, the DHCP
548// session is derived from the list of DHCP sessions stored in the
549// common map. The key for retrieval includes the VLAN tags in the
550// the packet and the MAC address of the client.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530551func (va *VoltApplication) ProcessDsDhcpv4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530552
553 // Retrieve the layers to build the outgoing packet. It is not
554 // possible to add/remove layers to the existing packet and thus
555 // the lyayers are extracted to build the outgoing packet
556 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
557 ip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
558 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
559 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
560 msgType := DhcpMsgType(dhcp4)
561
562 // Need to locate the service from the packet alone as the services
563 // are not attached to NNI port. The service is stored on DHCP relay
564 // application
565 logger.Infow(ctx, "Processing Southbound DS DHCPv4 packet", log.Fields{"Port": port, "Type": msgType})
566
567 // Retrieve the priority and drop eligible flags from the
568 // packet received
569 var priority uint8
570 var dsPbit uint8
571 var dropEligible bool
572 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
573 if dot1ql != nil {
574 dot1q := dot1ql.(*layers.Dot1Q)
575 priority = dot1q.Priority
576 dropEligible = dot1q.DropEligible
577 }
578
579 pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt)
580 vpvList, _ := GetVnetForV4Nni(dhcp4, pktInnerlan, pktOuterlan, priority)
581 if len(vpvList) == 0 {
582 logger.Warn(ctx, "VNET couldn't be found for NNI")
583 return
584 }
585
586 // The DHCP option 82, if it exists is removed from the packet
587 DelOption82(dhcp4)
588 ipAddr, leaseTime := GetIpv4Addr(dhcp4)
589
590 for _, vpv := range vpvList {
591 dsPbit = vpv.GetRemarkedPriority(priority)
592 // Raise DHCP ACK/NCK indication
593 if vpv.DhcpRelay {
594 // Inform dhcp response information to dhcp server handler
595 dhcpResponseReceived(uint16(vpv.CVlan), uint16(vpv.SVlan))
596 // Process the Ack/Nack to track to state of the IP layer of the connection
597 if msgType == layers.DHCPMsgTypeAck || msgType == layers.DHCPMsgTypeNak {
598 // Install DS HSIA flows after DHCP ACK.
599 if msgType == layers.DHCPMsgTypeAck {
600 // Voltha will push US and DS HSIA flow on receivng the DS HSIA
601 // flow installation request, VGC to update US HSIA flow with leanrt MAC.
602 // separate go rotuine is spawned to avoid drop of ACK packet
603 // as HSIA flows will be deleted if new MAC is learnt.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530604 go vpv.SetMacAddr(cntx, dhcp4.ClientHWAddr)
Naveen Sampath04696f72022-06-13 15:19:14 +0530605 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530606 vpv.DhcpResultInd(cntx, dhcp4)
Naveen Sampath04696f72022-06-13 15:19:14 +0530607
608 }
609 raiseDHCPv4Indication(msgType, vpv, dhcp4.ClientHWAddr, ipAddr, dsPbit, device, leaseTime)
610 }
611
612 // Create the outgoing bufer and set the checksum in the packet
613 buff := gopacket.NewSerializeBuffer()
614 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
615 logger.Error(ctx, "Error in setting checksum")
616 return
617 }
618 opts := gopacket.SerializeOptions{
619 FixLengths: true,
620 ComputeChecksums: true,
621 }
622
623 cTagType := layers.EthernetTypeIPv4
624 eth.EthernetType = layers.EthernetTypeDot1Q
625
626 var pktLayers []gopacket.SerializableLayer
627 pktLayers = append(pktLayers, eth)
628
629 var qVlans []of.VlanType
630 var qVlanLayers []gopacket.SerializableLayer
631
632 if vpv.AllowTransparent {
633 vlanThreshold := 2
634 // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan
635 // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan
636 // Based on that, the no. of vlans should be ignored to get only transparent vlans
637 if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None {
638 vlanThreshold = 1
639 }
640 nxtLayer := layers.EthernetTypeDot1Q
641 if vlans := GetVlans(pkt); len(vlans) > vlanThreshold {
642 qVlans = vlans[vlanThreshold:]
643 cTagType = layers.EthernetTypeDot1Q
644 }
645 for i, qVlan := range qVlans {
646 vlan := uint16(qVlan)
647 if i == (len(qVlans) - 1) {
648 nxtLayer = layers.EthernetTypeIPv4
649 }
650 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
651 qVlanLayers = append(qVlanLayers, qdot1q)
652 }
653 }
654 switch vpv.VlanControl {
655 case ONUCVlanOLTSVlan:
656 cdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType}
657 pktLayers = append(pktLayers, cdot1q)
658 case ONUCVlan,
659 None:
660 sdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType}
661 pktLayers = append(pktLayers, sdot1q)
662 case OLTCVlanOLTSVlan,
663 OLTSVlan:
664 udot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType}
665 pktLayers = append(pktLayers, udot1q)
666 default:
667 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
668 }
669
670 pktLayers = append(pktLayers, qVlanLayers...)
671 pktLayers = append(pktLayers, ip)
672 pktLayers = append(pktLayers, udp)
673 pktLayers = append(pktLayers, dhcp4)
674 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
675 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
676 logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()})
677 return
678 }
679
680 if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil {
681 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error" : err})
682 }
683 }
684}
685
686// raiseDHCPv4Indication process DHCPv4 packet and raise indication
687func raiseDHCPv4Indication(msgType layers.DHCPMsgType, vpv *VoltPortVnet, smac net.HardwareAddr,
688 ip net.IP, pktPbit uint8, device string, leaseTime int64) {
689
690 logger.Debugw(ctx, "Processing Dhcpv4 packet", log.Fields{"ethsrcMac": smac.String(),
691 "MacLearningInVPV": vpv.MacLearning, "MacConfigured": vpv.MacAddr, "dhcpType": msgType,
692 "vlanPriority": pktPbit, "VPVLearntMac": vpv.LearntMacAddr})
693
694 matchServiceAndRaiseInd := func(key, value interface{}) bool {
695 // walk through all svcs under vpv and match pbit with packet.
696 svc := value.(*VoltService)
697
698 if svc.IsPbitExist(of.PbitType(pktPbit)) {
699 logger.Debugw(ctx, "Matching Pbit found in service config", log.Fields{"ServiceName": svc.Name, "Pbit": pktPbit})
700 return false
701 }
702 return true
703 }
704
705 switch msgType {
706 case layers.DHCPMsgTypeDiscover, layers.DHCPMsgTypeRequest:
707 if msgType == layers.DHCPMsgTypeDiscover {
708 vpv.SetDhcpState(DhcpRelayStateDiscover)
709 } else if msgType == layers.DHCPMsgTypeRequest {
710 vpv.SetDhcpState(DhcpRelayStateRequest)
711 }
712 // Reset learnt mac address in case of DHCPv4 release
713 case layers.DHCPMsgTypeRelease:
714 vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
715 vpv.services.Range(matchServiceAndRaiseInd)
716 vpv.SetDhcpState(DhcpRelayStateRelease)
717
718 case layers.DHCPMsgTypeAck, layers.DHCPMsgTypeNak:
719 vpv.services.Range(matchServiceAndRaiseInd)
720 if msgType == layers.DHCPMsgTypeAck {
721 vpv.SetDhcpState(DhcpRelayStateAck)
722 } else if msgType == layers.DHCPMsgTypeNak {
723 vpv.SetDhcpState(DhcpRelayStateNAK)
724 }
725 case layers.DHCPMsgTypeOffer:
726 vpv.SetDhcpState(DhcpRelayStateOffer)
727 }
728}
729
730// raiseDHCPv6Indication process DHCPv6 packet and raise indication
731func raiseDHCPv6Indication(msgType layers.DHCPv6MsgType, vpv *VoltPortVnet,
732 smac net.HardwareAddr, ip net.IP, pktPbit uint8, device string, leaseTime uint32) {
733
734 logger.Debugw(ctx, "Processing DHCPv6 packet", log.Fields{"dhcpType": msgType,
735 "vlanPriority": pktPbit, "dhcpClientMac": smac.String(),
736 "MacLearningInVPV": vpv.MacLearning, "MacConfigured": vpv.MacAddr,
737 "VPVLearntMac": vpv.LearntMacAddr})
738
739 matchServiceAndRaiseInd := func(key, value interface{}) bool {
740 svc := value.(*VoltService)
741 if svc.IsPbitExist(of.PbitType(pktPbit)) {
742 logger.Debugw(ctx, "Matching Pbit found in service config", log.Fields{"ServiceName": svc.Name, "Pbit": pktPbit})
743 return false
744 }
745 return true
746 }
747
748 switch msgType {
749 case layers.DHCPv6MsgTypeSolicit:
750 vpv.SetDhcpv6State(Dhcpv6RelayStateSolicit)
751 // Reset learnt mac address in case of DHCPv6 release
752 case layers.DHCPv6MsgTypeRelease:
753 vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
754 vpv.services.Range(matchServiceAndRaiseInd)
755 vpv.SetDhcpv6State(Dhcpv6RelayStateRelease)
756
757 case layers.DHCPv6MsgTypeReply:
758 vpv.services.Range(matchServiceAndRaiseInd)
759 vpv.SetDhcpv6State(Dhcpv6RelayStateReply)
760 }
761}
762
763// ProcessUsDhcpv4Packet : The US DHCPv4 packet is identified the DHCP OP in the packet. A request is considered upstream
764// and the service associated with the packet is located by the port and VLANs in the packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530765func (va *VoltApplication) ProcessUsDhcpv4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530766 // We received the packet on an access port and the service for the packet can be
767 // gotten from the port and the packet
768 vpv, svc := va.GetVnetFromPkt(device, port, pkt)
769 if vpv == nil {
770 logger.Warn(ctx, "VNET couldn't be found from packet")
771 return
772 }
773
774 outport, _ := va.GetNniPort(device)
775 if outport == "" || outport == "0" {
776 logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport})
777 return
778 }
779
780 // Extract the layers in the packet to prepare the outgoing packet
781 // We use the layers to build the outgoing packet from scratch as
782 // the packet received can't be modified to add/remove layers
783 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
784 ip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
785 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
786 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
787 msgType := DhcpMsgType(dhcp4)
788 logger.Infow(ctx, "Processing Southbound US DHCPv4 packet", log.Fields{"Device": device, "Port": port, "Type": msgType})
789
790 // Learn the 8021P values from the packet received
791 var priority uint8
792 var dropEligible bool
793 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
794 if dot1ql != nil {
795 dot1q := dot1ql.(*layers.Dot1Q)
796 priority = dot1q.Priority
797 dropEligible = dot1q.DropEligible
798 }
799 // If this is the first message in the DHCP sequence, the service
800 // is added to the DHCP relay application. The reply packets locate
801 // the associated service/session from the relay application.
802 if msgType == layers.DHCPMsgTypeDiscover || msgType == layers.DHCPMsgTypeRequest {
803 if err := dhcpNws.AddDhcpSession(pkt, vpv); err != nil {
804 logger.Errorw(ctx, "Adding dhcp session failed", log.Fields{"Error": err})
805 }
806 }
807
808 // Raise mac-learnt(DHCP Discover) indication when mac learning is enabled and learnt mac
809 // is not same as received mac address. If mac learning disabled, we have mac address in the
810 // service configuration. Hence mac learnt indication is not raised
811 // Reset learnt mac address in case of DHCP release and raise the indication
812 if vpv.DhcpRelay {
813 // If this is the first message in the DHCP sequence, the service
814 // is added to the DHCP relay application. The reply packets locate
815 // the associated service/session from the relay application.
816 // DS HSIA flows will be added after DHCP ACK .
817 if msgType == layers.DHCPMsgTypeDiscover || msgType == layers.DHCPMsgTypeRequest {
818 if !util.MacAddrsMatch(vpv.MacAddr, dhcp4.ClientHWAddr) {
819 // MAC is different and relearning is disabled.
820 if NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn {
821 // update learnt mac for debug purpose
822 vpv.LearntMacAddr = dhcp4.ClientHWAddr
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530823 vpv.WriteToDb(cntx)
Naveen Sampath04696f72022-06-13 15:19:14 +0530824 logger.Warnw(ctx, "Dropping the packet Mac relearn is disabled",
825 log.Fields{"vpv.MacAddr": vpv.MacAddr, "LearntMac": dhcp4.ClientHWAddr})
826 return
827 }
828 expectedPort := va.GetMacInPortMap(dhcp4.ClientHWAddr)
829 if expectedPort != "" && expectedPort != vpv.Port {
830 logger.Errorw(ctx, "mac-learnt-from-different-port-ignoring-dhcp-message", log.Fields{"MsgType": msgType, "ExpectedPort": expectedPort, "ReceivedPort": vpv.Port, "LearntMacAdrr": vpv.MacAddr, "NewMacAdrr": dhcp4.ClientHWAddr.String()})
831 return
832 }
833 }
834 }
835 raiseDHCPv4Indication(msgType, vpv, dhcp4.ClientHWAddr, vpv.Ipv4Addr, priority, device, 0)
836
837 // Check IsOption82Disabled flag in configuration. if true(disabled), do not add option82 into dhcpv4 header.
838 // Remote id can be custom or mac address.
839 // If remote id is custom, then add service will carry the remote id
840 // If remote id is mac address, and if mac is configured, then add service will carry the remote id
841 // If remote id is mac address, in mac learning case, then mac has to be taken from dhcp packet
842 if !svc.IsOption82Disabled {
843 var remoteID []byte
844 if svc.RemoteIDType == string(MACAddress) {
845 remoteID = []byte((dhcp4.ClientHWAddr).String())
846 } else if svc.RemoteID != nil {
847 remoteID = svc.RemoteID
848 }
849 AddDhcpv4Option82(svc, remoteID, dhcp4)
850 }
851 }
852
853 buff := gopacket.NewSerializeBuffer()
854 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
855 logger.Error(ctx, "Error in setting checksum")
856 return
857 }
858 opts := gopacket.SerializeOptions{
859 FixLengths: true,
860 ComputeChecksums: true,
861 }
862
863 cTagType := layers.EthernetTypeIPv4
864 outerVlan, innerVlan := vpv.GetNniVlans()
865 logger.Debugw(ctx, "Vnet Vlans", log.Fields{"Svlan": outerVlan, "Cvlan": innerVlan})
866 eth.EthernetType = vpv.SVlanTpid
867
868 var pktLayers []gopacket.SerializableLayer
869 pktLayers = append(pktLayers, eth)
870
871 var qVlans []of.VlanType
872 var qVlanLayers []gopacket.SerializableLayer
873
874 if vpv.AllowTransparent {
875 nxtLayer := layers.EthernetTypeDot1Q
876 if vlans := GetVlans(pkt); len(vlans) > 1 {
877 qVlans = vlans[1:]
878 logger.Debugw(ctx, "Q Vlans", log.Fields{"Vlan List": qVlans})
879 cTagType = layers.EthernetTypeDot1Q
880 }
881 for i, qVlan := range qVlans {
882 vlan := uint16(qVlan)
883 if i == (len(qVlans) - 1) {
884 nxtLayer = layers.EthernetTypeIPv4
885 }
886 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
887 qVlanLayers = append(qVlanLayers, qdot1q)
888 }
889 }
890 switch vpv.VlanControl {
891 case ONUCVlanOLTSVlan,
892 OLTCVlanOLTSVlan:
893 sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q}
894 pktLayers = append(pktLayers, sdot1q)
895 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType}
896 pktLayers = append(pktLayers, cdot1q)
897 case ONUCVlan,
898 OLTSVlan,
899 None:
900 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType}
901 pktLayers = append(pktLayers, cdot1q)
902 default:
903 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
904 }
905
906 pktLayers = append(pktLayers, qVlanLayers...)
907 pktLayers = append(pktLayers, ip)
908 pktLayers = append(pktLayers, udp)
909 pktLayers = append(pktLayers, dhcp4)
910 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
911 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
912 return
913 }
914
915 // Now the packet constructed is output towards the switch to be emitted on
916 // the NNI port
917 if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil {
918 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error" : err})
919 }
920 if vpv.DhcpRelay {
921 // Inform dhcp request information to dhcp server handler
922 dhcpRequestReceived(uint16(vpv.CVlan), uint16(vpv.SVlan), eth.SrcMAC.String())
923 }
924}
925
926// ProcessUDP4Packet : CallBack function registered with application to handle DHCP packetIn
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530927func ProcessUDP4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
928 GetApplication().ProcessUDP4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530929}
930
931// ProcessUDP4Packet : The packet is a UDP packet and currently only DHCP relay application is supported
932// We determine the packet direction and process it based on the direction
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530933func (va *VoltApplication) ProcessUDP4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530934 // Currently DHCP is the only application supported by the application
935 // We check for DHCP before proceeding futher. In future, this could be
936 // based on registration and the callbacks
937 dhcpl := pkt.Layer(layers.LayerTypeDHCPv4)
938 if dhcpl == nil {
939 return
940 }
941 //logger.Debugw(ctx, "Received Packet In", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
942 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
943 if dhcp4.Operation == layers.DHCPOpRequest {
944 // This is treated as an upstream packet in the VOLT application
945 // as VOLT serves access subscribers who use DHCP to acquire IP
946 // address and these packets go upstream to the network
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530947 va.ProcessUsDhcpv4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530948 } else {
949 // This is a downstream packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530950 va.ProcessDsDhcpv4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530951 }
952
953}
954
955// ProcessUDP6Packet : CallBack function registered with application to handle DHCPv6 packetIn
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530956func ProcessUDP6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
957 GetApplication().ProcessUDP6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530958}
959
960// ProcessUDP6Packet : As a LDRA node, we expect to see only RelayReply from the DHCP server and we always
961// pack the received request and send it to the server as a RelayForward message
962// We expect to see Solicit, Request in the most normal cases. Before the lease expires
963// we should also see Renew. However, we should always pack the US message by adding
964// additional option that identifies to the server that the DHCP packet is forwarded
965// by an LDRA node.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530966func (va *VoltApplication) ProcessUDP6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) []byte {
Naveen Sampath04696f72022-06-13 15:19:14 +0530967 dhcpl := pkt.Layer(layers.LayerTypeDHCPv6)
968 if dhcpl == nil {
969 return nil
970 }
971 logger.Infow(ctx, "Processing DHCPv6 packet", log.Fields{"Port": port})
972 dhcpv6 := dhcpl.(*layers.DHCPv6)
973 switch dhcpv6.MsgType {
974 case layers.DHCPv6MsgTypeSolicit, layers.DHCPv6MsgTypeRequest, layers.DHCPv6MsgTypeRenew,
975 layers.DHCPv6MsgTypeRelease, layers.DHCPv6MsgTypeRebind, layers.DHCPv6MsgTypeInformationRequest,
976 layers.DHCPv6MsgTypeDecline:
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530977 va.ProcessUsDhcpv6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530978 case layers.DHCPv6MsgTypeAdvertise, layers.DHCPv6MsgTypeConfirm, layers.DHCPv6MsgTypeReconfigure:
979 logger.Warnw(ctx, "SouthBound DHCPv6 DS Messages Expected For a Relay Agent", log.Fields{"Type": dhcpv6.MsgType})
980 case layers.DHCPv6MsgTypeRelayForward:
981 logger.Warn(ctx, "As the first DHCPv6 Relay Agent, Unexpected Relay Forward")
982 case layers.DHCPv6MsgTypeRelayReply:
983 // We received a response from the server
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530984 va.ProcessDsDhcpv6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530985 }
986 return nil
987}
988
989// GetRelayReplyBytes to get relay reply bytes
990func GetRelayReplyBytes(dhcp6 *layers.DHCPv6) []byte {
991 for _, o := range dhcp6.Options {
992 logger.Infow(ctx, "Received Option", log.Fields{"Code": o.Code})
993 if o.Code == layers.DHCPv6OptRelayMessage {
994 return o.Data
995 }
996 }
997 return nil
998}
999
1000// BuildRelayFwd to build forward relay
1001func BuildRelayFwd(paddr net.IP, intfID []byte, remoteID []byte, payload []byte, isOption82Disabled bool, dhcpRelay bool) *layers.DHCPv6 {
1002 dhcp6 := &layers.DHCPv6{MsgType: layers.DHCPv6MsgTypeRelayForward, LinkAddr: net.ParseIP("::"), PeerAddr: []byte(paddr)}
1003 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptRelayMessage, payload))
1004 // Check IsOption82Disabled flag in configuration. if true(disabled), do not add remoteID and circuitID into dhcpv6 header.
1005 if dhcpRelay {
1006 if !isOption82Disabled {
1007 remote := &layers.DHCPv6RemoteId{RemoteId: remoteID}
1008 if len(remoteID) != 0 {
1009 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptRemoteID, remote.Encode()))
1010 }
1011 if len(intfID) != 0 {
1012 intf := &layers.DHCPv6IntfId{Data: intfID}
1013 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptInterfaceID, intf.Encode()))
1014 }
1015 }
1016 }
1017 return dhcp6
1018}
1019
1020// ProcessUsDhcpv6Packet to rpocess upstream DHCPv6 packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301021func (va *VoltApplication) ProcessUsDhcpv6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +05301022 // We received the packet on an access port and the service for the packet can be
1023 // gotten from the port and the packet
1024 logger.Infow(ctx, "Processing Southbound US DHCPv6 packet", log.Fields{"Port": port})
1025 logger.Debugw(ctx, "Packet IN", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
1026 vpv, svc := va.GetVnetFromPkt(device, port, pkt)
1027 if vpv == nil {
1028 logger.Warn(ctx, "VNET couldn't be found from packet")
1029 return
1030 }
1031
1032 outport, _ := va.GetNniPort(device)
1033 if outport == "" || outport == "0" {
1034 logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport})
1035 return
1036 }
1037
1038 // Extract the layers in the packet to prepare the outgoing packet
1039 // We use the layers to build the outgoing packet from scratch as
1040 // the packet received can't be modified to add/remove layers
1041 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
1042 ip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
1043 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
1044 idhcp6 := pkt.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
1045
1046 // Remote id can be custom or mac address.
1047 // If remote id is custom, then add service will carry the remote id
1048 // If remote id is mac address, and if mac is configured, then add service will carry the remote id
1049 // If remote id is mac address, in mac learning case, then mac has to be taken from dhcp packet
1050 var remoteID []byte
1051 if svc.RemoteIDType == string(MACAddress) {
1052 remoteID = []byte((eth.SrcMAC).String())
1053 } else if svc.RemoteID != nil {
1054 remoteID = svc.RemoteID
1055 }
1056 dhcp6 := BuildRelayFwd(ip.SrcIP, svc.GetCircuitID(), remoteID, udp.Payload, svc.IsOption82Disabled, vpv.DhcpRelay)
1057
1058 var sourceMac = eth.SrcMAC
1059 var sessionKey [MaxLenDhcpv6DUID]byte
1060
1061 clientDuid, decodedDuid := getDhcpv6ClientDUID(idhcp6)
1062 if clientDuid == nil || decodedDuid == nil {
1063 copy(sessionKey[:], eth.SrcMAC)
1064 } else {
1065 copy(sessionKey[:], clientDuid[0:])
1066 if decodedDuid.Type == layers.DHCPv6DUIDTypeLLT || decodedDuid.Type == layers.DHCPv6DUIDTypeLL {
1067 sourceMac = decodedDuid.LinkLayerAddress
1068 }
1069 }
1070 // Learn the 8021P values from the packet received
1071 var priority uint8
1072 var dropEligible bool
1073 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
1074 if dot1ql != nil {
1075 dot1q := dot1ql.(*layers.Dot1Q)
1076 priority = dot1q.Priority
1077 dropEligible = dot1q.DropEligible
1078 }
1079 if idhcp6.MsgType == layers.DHCPv6MsgTypeSolicit {
1080 if err := dhcpNws.AddDhcp6Session(sessionKey, vpv); err != nil {
1081 logger.Errorw(ctx, "Adding dhcpv6 session failed", log.Fields{"Error": err})
1082 }
1083 vpv.DHCPv6DUID = sessionKey
1084 }
1085
1086 // Raise mac-learnt(DHCPv6MsgTypeSolicit) indication when mac learning is enabled and learnt mac
1087 // is not same as received mac address. If mac learning disabled, we have mac address in the
1088 // service configuration. Hence mac learnt indication is not raised
1089 if vpv.DhcpRelay {
1090 if idhcp6.MsgType == layers.DHCPv6MsgTypeSolicit {
1091 if !util.MacAddrsMatch(vpv.MacAddr, sourceMac) {
1092 // MAC is different and relearning is disabled.
1093 if NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn {
1094 // update learnt mac for debug purpose
1095 vpv.LearntMacAddr = sourceMac
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301096 vpv.WriteToDb(cntx)
Naveen Sampath04696f72022-06-13 15:19:14 +05301097 logger.Warnw(ctx, "Dropping the packet Mac relearn is disabled",
1098 log.Fields{"vpv.MacAddr": vpv.MacAddr, "LearntMac": sourceMac})
1099 return
1100 }
1101 expectedPort := va.GetMacInPortMap(sourceMac)
1102 if expectedPort != "" && expectedPort != vpv.Port {
1103 logger.Errorw(ctx, "mac-learnt-from-different-port-ignoring-dhcp-message", log.Fields{"MsgType": idhcp6.MsgType, "ExpectedPort": expectedPort, "ReceivedPort": vpv.Port, "LearntMacAdrr": vpv.MacAddr, "NewMacAdrr": sourceMac.String()})
1104 return
1105 }
1106 }
1107 }
1108 raiseDHCPv6Indication(idhcp6.MsgType, vpv, sourceMac, vpv.Ipv6Addr, priority, device, 0)
1109 }
1110
1111 // Create the buffer and the encode options for the outgoing packet
1112 buff := gopacket.NewSerializeBuffer()
1113 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
1114 logger.Error(ctx, "Error in setting checksum")
1115 return
1116 }
1117 opts := gopacket.SerializeOptions{
1118 FixLengths: true,
1119 ComputeChecksums: true,
1120 }
1121
1122 cTagType := layers.EthernetTypeIPv6
1123 outerVlan, innerVlan := vpv.GetNniVlans()
1124 eth.EthernetType = vpv.SVlanTpid
1125
1126 var pktLayers []gopacket.SerializableLayer
1127 pktLayers = append(pktLayers, eth)
1128
1129 var qVlans []of.VlanType
1130 var qVlanLayers []gopacket.SerializableLayer
1131
1132 if vpv.AllowTransparent {
1133 nxtLayer := layers.EthernetTypeDot1Q
1134 if vlans := GetVlans(pkt); len(vlans) > 1 {
1135 qVlans = vlans[1:]
1136 cTagType = layers.EthernetTypeDot1Q
1137 }
1138 for i, qVlan := range qVlans {
1139 vlan := uint16(qVlan)
1140 if i == (len(qVlans) - 1) {
1141 nxtLayer = layers.EthernetTypeIPv6
1142 }
1143 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
1144 qVlanLayers = append(qVlanLayers, qdot1q)
1145 }
1146
1147 }
1148 switch vpv.VlanControl {
1149 case ONUCVlanOLTSVlan,
1150 OLTCVlanOLTSVlan:
1151 sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q}
1152 pktLayers = append(pktLayers, sdot1q)
1153 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType}
1154 pktLayers = append(pktLayers, cdot1q)
1155 case ONUCVlan,
1156 OLTSVlan,
1157 None:
1158 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType}
1159 pktLayers = append(pktLayers, cdot1q)
1160 default:
1161 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
1162 }
1163
1164 pktLayers = append(pktLayers, qVlanLayers...)
1165 pktLayers = append(pktLayers, ip)
1166 pktLayers = append(pktLayers, udp)
1167 pktLayers = append(pktLayers, dhcp6)
1168 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
1169 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
1170 return
1171 }
1172 // Now the packet constructed is output towards the switch to be emitted on
1173 // the NNI port
1174 if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil {
1175 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error" : err})
1176 }
1177 if vpv.DhcpRelay {
1178 // Inform dhcp request information to dhcp server handler
1179 dhcpRequestReceived(uint16(vpv.CVlan), uint16(vpv.SVlan), eth.SrcMAC.String())
1180 }
1181}
1182
1183// GetDhcpv6 to get dhcpv6 info
1184func GetDhcpv6(payload []byte) (*layers.DHCPv6, error) {
1185 pkt := gopacket.NewPacket(payload, layers.LayerTypeDHCPv6, gopacket.Default)
1186 if dl := pkt.Layer(layers.LayerTypeDHCPv6); dl != nil {
1187 if dhcp6, ok := dl.(*layers.DHCPv6); ok {
1188 return dhcp6, nil
1189 }
1190 }
1191 return nil, errors.New("Failed to decode DHCPv6")
1192}
1193
1194// ProcessDsDhcpv6Packet to process downstream dhcpv6 packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301195func (va *VoltApplication) ProcessDsDhcpv6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +05301196 logger.Infow(ctx, "Processing Southbound DS DHCPv6 packet", log.Fields{"Port": port})
1197 logger.Debugw(ctx, "Packet IN", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
1198
1199 // Retrieve the layers to build the outgoing packet. It is not
1200 // possible to add/remove layers to the existing packet and thus
1201 // the lyayers are extracted to build the outgoing packet
1202 // The DHCP layer is handled differently. The Relay-Reply option
1203 // of DHCP is extracted and is made the UDP payload.
1204 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
1205 ip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
1206 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
1207 idhcp6 := pkt.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
1208 //var dhcp6 *layers.DHCPv6
1209 var payload []byte
1210 if payload = GetRelayReplyBytes(idhcp6); payload == nil {
1211 logger.Warn(ctx, "Didn't Receive RelayMessage IE")
1212 return
1213 }
1214
1215 dhcp6, err := GetDhcpv6(payload)
1216 if err != nil {
1217 logger.Warnw(ctx, "DHCPv6 Decode Failed", log.Fields{"Reason": err.Error()})
1218 return
1219 }
1220
1221 // Learn the 8021P values from the packet received
1222 var priority uint8
1223 var dsPbit uint8
1224 var dropEligible bool
1225 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
1226 if dot1ql != nil {
1227 dot1q := dot1ql.(*layers.Dot1Q)
1228 priority = dot1q.Priority
1229 dropEligible = dot1q.DropEligible
1230 }
1231
1232 pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt)
1233 vpvList, clientMac, err := GetVnetForV6Nni(dhcp6, pktInnerlan, pktOuterlan, priority, eth.DstMAC)
1234 if len(vpvList) == 0 {
1235 logger.Warnw(ctx, "VNET couldn't be found for NNI", log.Fields{"Reason": err})
1236 return
1237 }
1238
1239 ipv6Addr, leaseTime := GetIpv6Addr(dhcp6)
1240
1241 for _, vpv := range vpvList {
1242
1243 dsPbit = vpv.GetRemarkedPriority(priority)
1244 // Raise DHCPv6 Reply indication
1245 if vpv.DhcpRelay {
1246 // Inform dhcp response information to dhcp server handler
1247 dhcpResponseReceived(uint16(vpv.CVlan), uint16(vpv.SVlan))
1248
1249 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply && ipv6Addr != nil {
1250 // separate go rotuine is spawned to avoid drop of ACK packet
1251 // as HSIA flows will be deleted if new MAC is learnt.
1252 if len(vpvList) == 1 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301253 go vpv.SetMacAddr(cntx, clientMac)
Naveen Sampath04696f72022-06-13 15:19:14 +05301254 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301255 vpv.Dhcpv6ResultInd(cntx, ipv6Addr, leaseTime)
Naveen Sampath04696f72022-06-13 15:19:14 +05301256 }
1257 raiseDHCPv6Indication(dhcp6.MsgType, vpv, clientMac, ipv6Addr, dsPbit, device, leaseTime)
1258 }
1259
1260 //Replace dst Port value to 546
1261 udp.DstPort = 546
1262 logger.Infow(ctx, "Packet Out UDP Port..", log.Fields{"UDP": udp, "Port": udp.DstPort})
1263
1264 // Create the buffer and the encode options for the outgoing packet
1265 buff := gopacket.NewSerializeBuffer()
1266 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
1267 logger.Error(ctx, "Error in setting checksum")
1268 return
1269 }
1270 opts := gopacket.SerializeOptions{
1271 FixLengths: true,
1272 ComputeChecksums: true,
1273 }
1274
1275 cTagType := layers.EthernetTypeIPv6
1276 eth.EthernetType = layers.EthernetTypeDot1Q
1277
1278 var pktLayers []gopacket.SerializableLayer
1279 pktLayers = append(pktLayers, eth)
1280
1281 var qVlans []of.VlanType
1282 var qVlanLayers []gopacket.SerializableLayer
1283
1284 if vpv.AllowTransparent {
1285 vlanThreshold := 2
1286 // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan
1287 // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan
1288 // Based on that, the no. of vlans should be ignored to get only transparent vlans
1289 if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None {
1290 vlanThreshold = 1
1291 }
1292 nxtLayer := layers.EthernetTypeDot1Q
1293 if vlans := GetVlans(pkt); len(vlans) > vlanThreshold {
1294 qVlans = vlans[vlanThreshold:]
1295 cTagType = layers.EthernetTypeDot1Q
1296 }
1297 for i, qVlan := range qVlans {
1298 vlan := uint16(qVlan)
1299 if i == (len(qVlans) - 1) {
1300 nxtLayer = layers.EthernetTypeIPv6
1301 }
1302 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
1303 qVlanLayers = append(qVlanLayers, qdot1q)
1304 }
1305
1306 }
1307 switch vpv.VlanControl {
1308 case ONUCVlanOLTSVlan:
1309 cdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType}
1310 pktLayers = append(pktLayers, cdot1q)
1311 case ONUCVlan,
1312 None:
1313 sdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType}
1314 pktLayers = append(pktLayers, sdot1q)
1315 case OLTCVlanOLTSVlan,
1316 OLTSVlan:
1317 udot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType}
1318 pktLayers = append(pktLayers, udot1q)
1319 default:
1320 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
1321 }
1322
1323 pktLayers = append(pktLayers, qVlanLayers...)
1324 pktLayers = append(pktLayers, ip)
1325 pktLayers = append(pktLayers, udp)
1326 pktLayers = append(pktLayers, dhcp6)
1327 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
1328 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
1329 logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()})
1330 return
1331 }
1332
1333 if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil {
1334 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Reason": err.Error()})
1335 }
1336 }
1337}
1338
1339// The DHCP relay application is maintained within the structures below
1340var dhcpNws *DhcpNetworks
1341
1342func init() {
1343 dhcpNws = NewDhcpNetworks()
1344}