blob: 0e1f138218b36b46360fce862f44996d2cfa7f45 [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 (
Tinoj Joseph07cc5372022-07-18 22:53:51 +053019 "context"
Akash Sonib3abf522022-12-19 13:20:02 +053020 "encoding/hex"
Naveen Sampath04696f72022-06-13 15:19:14 +053021 "errors"
22 "net"
23 "sync"
Tinoj Josephec742f62022-09-29 19:11:10 +053024 "time"
Naveen Sampath04696f72022-06-13 15:19:14 +053025
26 "github.com/google/gopacket"
27 "github.com/google/gopacket/layers"
28
29 cntlr "voltha-go-controller/internal/pkg/controller"
30 "voltha-go-controller/internal/pkg/of"
31 "voltha-go-controller/internal/pkg/util"
Tinoj Joseph1d108322022-07-13 10:07:39 +053032 "voltha-go-controller/log"
Naveen Sampath04696f72022-06-13 15:19:14 +053033)
34
35// DhcpRelayState type
36type DhcpRelayState uint8
37
38const (
39 // DhcpRelayStateNone constant
40 DhcpRelayStateNone DhcpRelayState = iota
41 // DhcpRelayStateDiscover constant
42 DhcpRelayStateDiscover
43 // DhcpRelayStateOffer constant
44 DhcpRelayStateOffer
45 // DhcpRelayStateRequest constant
46 DhcpRelayStateRequest
47 // DhcpRelayStateAck constant
48 DhcpRelayStateAck
49 // DhcpRelayStateNAK constant
50 DhcpRelayStateNAK
51 // DhcpRelayStateRelease constant
52 DhcpRelayStateRelease
53)
54
55// RemoteIDType represents data type for various RemoteID types
56type RemoteIDType string
57
58// List of RemoteID types supported
59const (
Akash Sonib3abf522022-12-19 13:20:02 +053060 MACAddress RemoteIDType = "MAC_ADDRESS"
61 CustomRemotedID RemoteIDType = "Custom"
Naveen Sampath04696f72022-06-13 15:19:14 +053062)
63
64// MaxLenDhcpv6DUID constant
65const MaxLenDhcpv6DUID = 130 // 2: DUID-Type, 128: MaxLen of DUID value
66
67// opt82 constant
68const opt82 = 82
69
70// Dhcpv6RelayState type
71type Dhcpv6RelayState uint8
72
73const (
74 // Dhcpv6RelayStateNone constant
75 Dhcpv6RelayStateNone Dhcpv6RelayState = iota
76 // Dhcpv6RelayStateSolicit constant
77 Dhcpv6RelayStateSolicit
78 // Dhcpv6RelayStateReply constant
79 Dhcpv6RelayStateReply
80 // Dhcpv6RelayStateRelease constant
81 Dhcpv6RelayStateRelease
82)
83
84var (
85 // ErrSessionDoNotExist error type
86 ErrSessionDoNotExist = errors.New("Session Doesn't Exist")
87)
88
89// IDhcpRelaySession to get dhcp session field value
90type IDhcpRelaySession interface {
91 GetCircuitID() []byte
92 GetRemoteID() []byte
93 GetNniVlans() (uint16, uint16)
94 GetDhcpState() DhcpRelayState
95 GetDhcpv6State() Dhcpv6RelayState
96 SetDhcpState(DhcpRelayState)
97 SetDhcpv6State(Dhcpv6RelayState)
Tinoj Joseph07cc5372022-07-18 22:53:51 +053098 SetMacAddr(context.Context, net.HardwareAddr)
99 DhcpResultInd(context.Context, *layers.DHCPv4)
100 Dhcpv6ResultInd(cntx context.Context, ipv6Addr net.IP, leaseTime uint32)
Naveen Sampath04696f72022-06-13 15:19:14 +0530101}
102
103// DhcpRelayVnet : The DHCP relay sessions are stored in a map to be retrieved from when
104// a response is received from the network. The map uses the VLANs and the
105// the MAC address as key to finding the service
106// DHCP Relay Virtual Network hosts a set of DHCP relay sessions that belong
107// to the network. It supports two VLANs as its identify. If a single VLAN or
108// no VLAN is to be used, those two should be passed as 4096 (VlanNone)
109type DhcpRelayVnet struct {
Naveen Sampath04696f72022-06-13 15:19:14 +0530110 sessions map[[6]byte]IDhcpRelaySession
111 sessionsv6 map[[MaxLenDhcpv6DUID]byte]IDhcpRelaySession
112 sessionLock sync.RWMutex
vinokuma926cb3e2023-03-29 11:41:06 +0530113 OuterVlan uint16
114 InnerVlan uint16
Naveen Sampath04696f72022-06-13 15:19:14 +0530115}
116
117// DhcpNetworks hosts different DHCP networks that in turn hold the DHCP
118// sessions
119type DhcpNetworks struct {
120 Networks map[uint32]*DhcpRelayVnet
121}
122
123func init() {
124 RegisterPacketHandler(DHCPv4, ProcessUDP4Packet)
125 RegisterPacketHandler(DHCPv6, ProcessUDP6Packet)
126}
127
128// NewDhcpRelayVnet is constructor for a DHCP Relay Virtual network
129func NewDhcpRelayVnet(outerVlan uint16, innerVlan uint16) *DhcpRelayVnet {
130 var drv DhcpRelayVnet
131
132 drv.OuterVlan = outerVlan
133 drv.InnerVlan = innerVlan
134 drv.sessions = make(map[[6]byte]IDhcpRelaySession)
135 drv.sessionsv6 = make(map[[MaxLenDhcpv6DUID]byte]IDhcpRelaySession)
136 return &drv
137}
138
139// GetDhcpVnet to add dhcp vnet
140func (dn *DhcpNetworks) GetDhcpVnet(outerVlan uint16, innerVlan uint16) *DhcpRelayVnet {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530141 logger.Debugw(ctx, "Get Dhcp Vnet", log.Fields{"OuterVlan": outerVlan, "InnerVlan": innerVlan})
Naveen Sampath04696f72022-06-13 15:19:14 +0530142 comboVlan := uint32(outerVlan)<<16 + uint32(innerVlan)
143 drv, ok := dn.Networks[comboVlan]
144 if ok {
145 return drv
146 }
147 return nil
148}
149
150// AddDhcpVnet to add dhcp vnet
151func (dn *DhcpNetworks) AddDhcpVnet(outerVlan uint16, innerVlan uint16) *DhcpRelayVnet {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530152 logger.Debugw(ctx, "Add Dhcp Vnet", log.Fields{"OuterVlan": outerVlan, "InnerVlan": innerVlan})
Naveen Sampath04696f72022-06-13 15:19:14 +0530153 comboVlan := uint32(outerVlan)<<16 + uint32(innerVlan)
154 if drv, ok := dn.Networks[comboVlan]; ok {
155 return drv
156 }
157 drv := NewDhcpRelayVnet(outerVlan, innerVlan)
158 dn.Networks[comboVlan] = drv
159 return drv
160}
161
162// NewDhcpNetworks to get new dhcp network
163func NewDhcpNetworks() *DhcpNetworks {
164 var dn DhcpNetworks
165 dn.Networks = make(map[uint32]*DhcpRelayVnet)
166 return &dn
167}
168
169// AddDhcpSession to add dhcp session
170func (dn *DhcpNetworks) AddDhcpSession(pkt gopacket.Packet, session IDhcpRelaySession) error {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530171 logger.Info(ctx, "Add Dhcp Session")
Naveen Sampath04696f72022-06-13 15:19:14 +0530172 var key [6]byte
173 ethl := pkt.Layer(layers.LayerTypeEthernet)
174 eth, _ := ethl.(*layers.Ethernet)
175 addr := eth.SrcMAC
176 if len(addr) != 6 {
177 logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
178 return errors.New("Invalid MAC address")
179 }
180 copy(key[:], addr[0:6])
181
182 drv := dn.AddDhcpVnet(session.GetNniVlans())
183
184 drv.sessionLock.Lock()
185 drv.sessions[key] = session
186 drv.sessionLock.Unlock()
187 return nil
188}
189
190// DelDhcpSession to delete dhcp session
191func (dn *DhcpNetworks) DelDhcpSession(pkt gopacket.Packet, session IDhcpRelaySession) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530192 logger.Info(ctx, "Delete Dhcp Session")
Naveen Sampath04696f72022-06-13 15:19:14 +0530193 var key [6]byte
194 ethl := pkt.Layer(layers.LayerTypeEthernet)
195 eth, _ := ethl.(*layers.Ethernet)
196 addr := eth.SrcMAC
197 if len(addr) != 6 {
198 logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
199 return
200 }
201 copy(key[:], addr[0:6])
202 drv := dn.AddDhcpVnet(session.GetNniVlans())
203 drv.sessionLock.Lock()
204 delete(drv.sessions, key)
205 drv.sessionLock.Unlock()
206}
207
208// delDhcpSessions to delete dhcp sessions
209func delDhcpSessions(addr net.HardwareAddr, outervlan of.VlanType, innervlan of.VlanType, sessionKey [MaxLenDhcpv6DUID]byte) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530210 logger.Debugw(ctx, "Delete Dhcp Sessions", log.Fields{"Addr": addr, "OuterVlan": outervlan, "InnerVlan": innervlan})
Naveen Sampath04696f72022-06-13 15:19:14 +0530211 var key [6]byte
212 if addr == nil || !NonZeroMacAddress(addr) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530213 logger.Warnw(ctx, "Invalid MAC address", log.Fields{"Addr": addr, "OuterVlan": outervlan, "InnerVlan": innervlan})
Naveen Sampath04696f72022-06-13 15:19:14 +0530214 return
215 }
216 copy(key[:], addr[0:6])
217 drv := dhcpNws.AddDhcpVnet(uint16(outervlan), uint16(innervlan))
218 drv.sessionLock.Lock()
219 delete(drv.sessions, key)
220 delete(drv.sessionsv6, sessionKey)
221 drv.sessionLock.Unlock()
balaji.nagarajan182b64f2025-09-04 11:25:17 +0530222 logger.Debugw(ctx, "DHCP Sessions deleted", log.Fields{"MAC": addr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530223}
224
225// AddDhcp6Session to add dhcpv6 session
226func (dn *DhcpNetworks) AddDhcp6Session(key [MaxLenDhcpv6DUID]byte, session IDhcpRelaySession) error {
227 outerVlan, innerVlan := session.GetNniVlans()
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530228 logger.Debugw(ctx, "Adding Dhcp6 Session", log.Fields{"outerVlan": outerVlan, "innerVlan": innerVlan, "Addr": key})
Naveen Sampath04696f72022-06-13 15:19:14 +0530229 drv := dn.AddDhcpVnet(outerVlan, innerVlan)
230 drv.sessionLock.Lock()
231 drv.sessionsv6[key] = session
232 drv.sessionLock.Unlock()
233 return nil
234}
235
236// DelDhcp6Session to delete dhcpv6 session
237func (dn *DhcpNetworks) DelDhcp6Session(key [MaxLenDhcpv6DUID]byte, session IDhcpRelaySession) {
238 outerVlan, innerVlan := session.GetNniVlans()
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530239 logger.Debugw(ctx, "Delete Dhcp6 Session", log.Fields{"OuterVLAN": outerVlan, "InnerVLAN": innerVlan, "Addr": key})
Naveen Sampath04696f72022-06-13 15:19:14 +0530240 drv := dn.GetDhcpVnet(outerVlan, innerVlan)
241 drv.sessionLock.Lock()
242 delete(drv.sessionsv6, key)
243 drv.sessionLock.Unlock()
244}
245
246// GetDhcpSession to get dhcp session info
247func (dn *DhcpNetworks) GetDhcpSession(outerVlan uint16, innerVlan uint16, addr net.HardwareAddr) (IDhcpRelaySession, error) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530248 logger.Debugw(ctx, "Get Dhcp Session", log.Fields{"OuterVLAN": outerVlan, "InnerVLAN": innerVlan, "Addr": addr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530249 var key [6]byte
250 if len(addr) != 6 {
251 logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr})
252 return nil, errors.New("Invalid MAC address")
253 }
254 copy(key[:], addr[0:6])
255 drv := dn.AddDhcpVnet(outerVlan, innerVlan)
256 drv.sessionLock.RLock()
257 defer drv.sessionLock.RUnlock()
258 if session, ok := drv.sessions[key]; ok {
259 return session, nil
260 }
261 return nil, ErrSessionDoNotExist
262}
263
264// GetDhcp6Session to get Dhcp6Session
265func (dn *DhcpNetworks) GetDhcp6Session(outerVlan uint16, innerVlan uint16, key [MaxLenDhcpv6DUID]byte) (IDhcpRelaySession, error) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530266 logger.Debugw(ctx, "Locating Session", log.Fields{"OuterVlan": outerVlan, "InnerVlan": innerVlan, "key": key})
Naveen Sampath04696f72022-06-13 15:19:14 +0530267
268 drv := dn.AddDhcpVnet(outerVlan, innerVlan)
269 drv.sessionLock.RLock()
270 defer drv.sessionLock.RUnlock()
271 if session, ok := drv.sessionsv6[key]; ok {
272 return session, nil
273 }
274 return nil, ErrSessionDoNotExist
275}
276
277// GetVlansFromPacket to get vlans from the packet
278func GetVlansFromPacket(pkt gopacket.Packet) (innerVlan of.VlanType, outerVlan of.VlanType) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530279 logger.Debugw(ctx, "Get Vlans From Packet", log.Fields{"OuterVlan": outerVlan, "InnerVlan": innerVlan})
Naveen Sampath04696f72022-06-13 15:19:14 +0530280 vlans := GetVlans(pkt)
281 if len(vlans) == 1 {
282 outerVlan = vlans[0]
283 innerVlan = of.VlanNone
284 } else if len(vlans) == 0 {
285 innerVlan = of.VlanNone
286 outerVlan = of.VlanNone
287 } else {
288 innerVlan = vlans[1]
289 outerVlan = vlans[0]
290 }
291 return
292}
293
294// GetVnetForV4Nni to get vnet for v4 Nni
295func GetVnetForV4Nni(dhcp *layers.DHCPv4, cvlan of.VlanType, svlan of.VlanType, pbit uint8) ([]*VoltPortVnet, error) {
296 var err error
297 var session IDhcpRelaySession
298 var vpvList []*VoltPortVnet
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530299 logger.Debugw(ctx, "Get Vnet For V4 Nni: ", log.Fields{"Addr": dhcp.ClientHWAddr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530300 session, err = dhcpNws.GetDhcpSession(uint16(svlan), uint16(cvlan), dhcp.ClientHWAddr)
301
302 if session != nil {
303 vpv, ok := session.(*VoltPortVnet)
304 logger.Infow(ctx, "Session Exist: VPV found", log.Fields{"VPV": vpv})
305 if ok {
306 vpvList = append(vpvList, vpv)
307 return vpvList, nil
308 }
309 }
310
311 if err == ErrSessionDoNotExist {
312 //No DHCP Session found, find matching VPV to send the packet out
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530313 logger.Warn(ctx, "Session Doesnt Exist: Finding matching VPV")
Naveen Sampath04696f72022-06-13 15:19:14 +0530314 return GetApplication().GetVpvsForDsPkt(cvlan, svlan, dhcp.ClientHWAddr, pbit)
315 }
316 return nil, errors.New("The session retrieved of wrong type")
317}
318
319// GetVnetForV6Nni to get vnet for v6 Nni
320func GetVnetForV6Nni(dhcp *layers.DHCPv6, cvlan of.VlanType, svlan of.VlanType,
321 pbit uint8, clientMAC net.HardwareAddr) ([]*VoltPortVnet, net.HardwareAddr, error) {
322 var err error
323 var session IDhcpRelaySession
324 var vpvList []*VoltPortVnet
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530325 logger.Info(ctx, "Get Vnet For V6 Nni")
Naveen Sampath04696f72022-06-13 15:19:14 +0530326
327 var sessionKey [MaxLenDhcpv6DUID]byte
328
329 clientDuid, decodedDuid := getDhcpv6ClientDUID(dhcp)
330 if clientDuid == nil || decodedDuid == nil {
331 copy(sessionKey[:], clientMAC)
332 } else {
333 copy(sessionKey[:], clientDuid[0:])
334 if decodedDuid.Type == layers.DHCPv6DUIDTypeLLT || decodedDuid.Type == layers.DHCPv6DUIDTypeLL {
335 clientMAC = decodedDuid.LinkLayerAddress
336 }
337 }
338 session, err = dhcpNws.GetDhcp6Session(uint16(svlan), uint16(cvlan), sessionKey)
339 if session != nil {
340 vpv, ok := session.(*VoltPortVnet)
341 logger.Infow(ctx, "Session Exist: VPV found", log.Fields{"VPV": vpv})
342 if ok {
343 vpvList = append(vpvList, vpv)
344 return vpvList, clientMAC, nil
345 }
346 }
347
348 if err == ErrSessionDoNotExist {
349 //No DHCP Session found, find matching VPV to send the packet out
350 logger.Info(ctx, "Session Doesnt Exist: Finding matching VPV")
351 vpvList, err := GetApplication().GetVpvsForDsPkt(cvlan, svlan, clientMAC, pbit)
352 return vpvList, clientMAC, err
353 }
354 return nil, clientMAC, errors.New("The session retrieved of wrong type")
355}
356
357/*
358// getDhcpv4ClientMacAddr to get mac address for dhcpv4 client
359func getDhcpv4ClientMacAddr(pkt gopacket.Packet) net.HardwareAddr {
360 dhcp := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
361 logger.Infow(ctx, "Mac Obtained v4: ", log.Fields{"Addr": dhcp.ClientHWAddr})
362 return dhcp.ClientHWAddr
363}
364
365// getDhcpv6ClientMacAddr to get mac address for dhcpv6 client
366func getDhcpv6ClientMacAddr(dhcpv6 *layers.DHCPv6) net.HardwareAddr {
367 var cID layers.DHCPv6Option
368 for _, option := range dhcpv6.Options {
369 if option.Code == layers.DHCPv6OptClientID {
370 cID = option
371 }
372 }
373 duid := &layers.DHCPv6DUID{}
374
375 //If cID is not found, DecodeFromBytes() returns error on empty cID
376 if err := duid.DecodeFromBytes(cID.Data); err == nil {
377 logger.Infow(ctx, "Mac Obtained v6: ", log.Fields{"Addr": duid.LinkLayerAddress, "Option": cID.String()})
378 return duid.LinkLayerAddress
379 }
380 return nil
381}*/
382
383// getDhcpv6ClientDUID to get Dhcpv6 client DUID
384func getDhcpv6ClientDUID(dhcpv6 *layers.DHCPv6) ([]byte, *layers.DHCPv6DUID) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530385 logger.Info(ctx, "Get Dhcp v6 Client DUID")
Naveen Sampath04696f72022-06-13 15:19:14 +0530386 for _, option := range dhcpv6.Options {
387 logger.Debugw(ctx, "DHCPv6 Options", log.Fields{"option": option.Code})
388 if option.Code == layers.DHCPv6OptClientID {
389 duid := &layers.DHCPv6DUID{}
390 err := duid.DecodeFromBytes(option.Data)
391 if err == nil {
balaji.nagarajan182b64f2025-09-04 11:25:17 +0530392 logger.Debugw(ctx, "ClientIdentifier", log.Fields{"DUID": duid, "Option": option.String()})
Naveen Sampath04696f72022-06-13 15:19:14 +0530393 duidLen := len(option.Data)
394 if duidLen > 130 {
395 duidLen = 130
396 }
397 return option.Data[0:duidLen], duid
398 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530399 logger.Warnw(ctx, "Client DUID decode failed", log.Fields{"error": err})
Naveen Sampath04696f72022-06-13 15:19:14 +0530400 break
401 }
402 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530403 logger.Warn(ctx, "Client DUID is not present in the packet")
Naveen Sampath04696f72022-06-13 15:19:14 +0530404 return nil, nil
405}
406
407// AddDhcpv4Option82 : DHCPv4 packet operations
408// Addition of DHCP Option 82 which codes circuit-id and remote-id
409// into the packet. This happens as the request is relayed to the
410// DHCP servers on the NNI
411func AddDhcpv4Option82(svc *VoltService, rID []byte, dhcpv4 *layers.DHCPv4) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530412 logger.Debugw(ctx, "Add Dhcp v4 Option82", log.Fields{"Addr": dhcpv4.ClientHWAddr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530413 //NOTE : both cID and rID should not be empty if this function is called
414 cID := svc.GetCircuitID()
415 var data []byte
416 if len(cID) != 0 {
417 data = append(data, 0x01)
418 data = append(data, byte(len(cID)))
419 data = append(data, cID...)
420 }
421 if len(rID) != 0 {
422 data = append(data, 0x02)
423 data = append(data, byte(len(rID)))
424 data = append(data, rID...)
425 }
426
427 if svc.isDataRateAttrPresent() {
428 minDrUs := util.Uint32ToByte(svc.MinDataRateUs)
429 data = append(data, TYPEMINDATAUS)
430 data = append(data, byte(len(minDrUs)))
431 data = append(data, minDrUs...)
432
433 minDrDs := util.Uint32ToByte(svc.MinDataRateDs)
434 data = append(data, TYPEMINDATADS)
435 data = append(data, byte(len(minDrDs)))
436 data = append(data, minDrDs...)
437
438 maxDrUs := util.Uint32ToByte(svc.MaxDataRateUs)
439 data = append(data, TYPEMAXDATAUS)
440 data = append(data, byte(len(maxDrUs)))
441 data = append(data, maxDrUs...)
442
443 maxDrDs := util.Uint32ToByte(svc.MaxDataRateDs)
444 data = append(data, TYPEMAXDATADS)
445 data = append(data, byte(len(maxDrDs)))
446 data = append(data, maxDrDs...)
447 }
448
449 option := layers.NewDHCPOption(82, data)
450 dhcpv4.Options = append(dhcpv4.Options, option)
451}
452
453// DelOption82 : Deletion of option 82 from the packet received on the NNI interface.
454// Once the packet is received, the option 82 is stripped off and the
455// packet is forwarded towards access
456func DelOption82(dhcpv4 *layers.DHCPv4) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530457 logger.Debugw(ctx, "Delete Dhcp v4 Option82", log.Fields{"Addr": dhcpv4.ClientHWAddr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530458 for index, option := range dhcpv4.Options {
459 if option.Type == opt82 {
460 dhcpv4.Options = append(dhcpv4.Options[0:index], dhcpv4.Options[index+1:]...)
461 return
462 }
463 }
464}
465
466// DhcpMsgType returns the DHCP message type from the packet
467func DhcpMsgType(dhcp *layers.DHCPv4) layers.DHCPMsgType {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530468 logger.Debugw(ctx, "Dhcp msg type", log.Fields{"Addr": dhcp.ClientHWAddr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530469 for _, option := range dhcp.Options {
470 if option.Type == layers.DHCPOptMessageType {
471 return layers.DHCPMsgType(option.Data[0])
472 }
473 }
474 return layers.DHCPMsgTypeUnspecified
475}
476
477// GetIpv4Addr returns the IP address in the DHCP reply
478func GetIpv4Addr(dhcp *layers.DHCPv4) (net.IP, int64) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530479 logger.Debugw(ctx, "Get Dhcp ipv4 addr", log.Fields{"Addr": dhcp.ClientHWAddr})
Naveen Sampath04696f72022-06-13 15:19:14 +0530480 var leaseTime uint32
481 for _, opt := range dhcp.Options {
482 if opt.Type == layers.DHCPOptLeaseTime {
483 leaseTime = GetIPv4LeaseTime(opt)
484 }
485 }
486 return dhcp.YourClientIP, int64(leaseTime)
487}
488
vinokuma926cb3e2023-03-29 11:41:06 +0530489// GetIPv4LeaseTime get ip lease time
Naveen Sampath04696f72022-06-13 15:19:14 +0530490func GetIPv4LeaseTime(opt layers.DHCPOption) uint32 {
491 return uint32(opt.Data[0])<<24 | uint32(opt.Data[1])<<16 | uint32(opt.Data[2])<<8 | uint32(opt.Data[3])
492}
493
494// GetIpv6Addr returns the IPv6 address in the DHCPv6 reply
495func GetIpv6Addr(dhcp6 *layers.DHCPv6) (net.IP, uint32) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530496 logger.Debugw(ctx, "Get Dhcp ipv6 addr", log.Fields{"Addr": dhcp6.MsgType})
Naveen Sampath04696f72022-06-13 15:19:14 +0530497 var ipv6Addr net.IP
498 var leaseTime uint32
499
500 //Check for IANA allocation, if not present, then look for IAPD allocation
501 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply {
502 ipv6Addr, leaseTime = GetIANAAddress(dhcp6)
503 if ipv6Addr == nil {
504 ipv6Addr, leaseTime = GetIAPDAddress(dhcp6)
505 }
506 }
507 return ipv6Addr, leaseTime
508}
509
510// GetIANAAddress returns the IPv6 address in the DHCPv6 reply
511func GetIANAAddress(dhcp6 *layers.DHCPv6) (net.IP, uint32) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530512 logger.Debugw(ctx, "Get Dhcp IANA addr", log.Fields{"Addr": dhcp6.MsgType})
Naveen Sampath04696f72022-06-13 15:19:14 +0530513 var ipv6Addr net.IP
514 var leaseTime uint32
515 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply {
516 for _, o := range dhcp6.Options {
517 if o.Code == layers.DHCPv6OptIANA {
Naveen Sampath04696f72022-06-13 15:19:14 +0530518 iana := &layers.DHCPv6IANA{}
519 err := iana.DecodeFromBytes(o.Data)
520 if err == nil {
521 ipv6Addr = iana.IA.IPv6Addr
522 leaseTime = iana.IA.ValidLifeTime
523 logger.Debugw(ctx, "IPv6 Allocated", log.Fields{"IANA IPv6": ipv6Addr})
524 return ipv6Addr, leaseTime
525 }
Tinoj Joseph1d108322022-07-13 10:07:39 +0530526 logger.Warnw(ctx, "Decode of IANA Failed", log.Fields{"Reason": err.Error()})
Naveen Sampath04696f72022-06-13 15:19:14 +0530527 break
528 }
529 }
530 }
531 return nil, 0
532}
533
534// GetIAPDAddress returns the IPv6 address in the DHCPv6 reply
535func GetIAPDAddress(dhcp6 *layers.DHCPv6) (net.IP, uint32) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530536 logger.Debugw(ctx, "Get Dhcp IAPD addr", log.Fields{"Addr": dhcp6.MsgType})
Naveen Sampath04696f72022-06-13 15:19:14 +0530537 var ipv6Addr net.IP
538 var leaseTime uint32
539 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply {
540 for _, o := range dhcp6.Options {
541 if o.Code == layers.DHCPv6OptIAPD {
Naveen Sampath04696f72022-06-13 15:19:14 +0530542 iapd := &layers.DHCPv6IAPD{}
543 if err := iapd.DecodeFromBytes(o.Data); err == nil {
544 ipv6Addr = iapd.PD.Prefix
545 leaseTime = iapd.PD.ValidLifeTime
546 logger.Debugw(ctx, "IPv6 Allocated", log.Fields{"IAPD IPv6": ipv6Addr})
547 break
548 } else {
Tinoj Joseph1d108322022-07-13 10:07:39 +0530549 logger.Warnw(ctx, "Decode of IAPD Failed", log.Fields{"Reason": err.Error()})
Naveen Sampath04696f72022-06-13 15:19:14 +0530550 break
551 }
552 }
553 }
554 }
555 return ipv6Addr, leaseTime
556}
557
558// ProcessDsDhcpv4Packet : DHCPv4 packet processor functions
559// This function processes DS DHCP packet received on the NNI port.
560// The services are attached to the access ports. Thus, the DHCP
561// session is derived from the list of DHCP sessions stored in the
562// common map. The key for retrieval includes the VLAN tags in the
563// the packet and the MAC address of the client.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530564func (va *VoltApplication) ProcessDsDhcpv4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530565 // Retrieve the layers to build the outgoing packet. It is not
566 // possible to add/remove layers to the existing packet and thus
567 // the lyayers are extracted to build the outgoing packet
568 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
569 ip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
570 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
571 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
572 msgType := DhcpMsgType(dhcp4)
573
574 // Need to locate the service from the packet alone as the services
575 // are not attached to NNI port. The service is stored on DHCP relay
576 // application
577 logger.Infow(ctx, "Processing Southbound DS DHCPv4 packet", log.Fields{"Port": port, "Type": msgType})
578
579 // Retrieve the priority and drop eligible flags from the
580 // packet received
581 var priority uint8
582 var dsPbit uint8
583 var dropEligible bool
584 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
585 if dot1ql != nil {
586 dot1q := dot1ql.(*layers.Dot1Q)
587 priority = dot1q.Priority
588 dropEligible = dot1q.DropEligible
589 }
590
591 pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt)
592 vpvList, _ := GetVnetForV4Nni(dhcp4, pktInnerlan, pktOuterlan, priority)
593 if len(vpvList) == 0 {
594 logger.Warn(ctx, "VNET couldn't be found for NNI")
595 return
596 }
597
598 // The DHCP option 82, if it exists is removed from the packet
599 DelOption82(dhcp4)
600 ipAddr, leaseTime := GetIpv4Addr(dhcp4)
601
602 for _, vpv := range vpvList {
603 dsPbit = vpv.GetRemarkedPriority(priority)
604 // Raise DHCP ACK/NCK indication
605 if vpv.DhcpRelay {
606 // Inform dhcp response information to dhcp server handler
607 dhcpResponseReceived(uint16(vpv.CVlan), uint16(vpv.SVlan))
608 // Process the Ack/Nack to track to state of the IP layer of the connection
609 if msgType == layers.DHCPMsgTypeAck || msgType == layers.DHCPMsgTypeNak {
610 // Install DS HSIA flows after DHCP ACK.
611 if msgType == layers.DHCPMsgTypeAck {
612 // Voltha will push US and DS HSIA flow on receivng the DS HSIA
613 // flow installation request, VGC to update US HSIA flow with leanrt MAC.
614 // separate go rotuine is spawned to avoid drop of ACK packet
615 // as HSIA flows will be deleted if new MAC is learnt.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530616 go vpv.SetMacAddr(cntx, dhcp4.ClientHWAddr)
Naveen Sampath04696f72022-06-13 15:19:14 +0530617 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530618 vpv.DhcpResultInd(cntx, dhcp4)
Naveen Sampath04696f72022-06-13 15:19:14 +0530619 }
620 raiseDHCPv4Indication(msgType, vpv, dhcp4.ClientHWAddr, ipAddr, dsPbit, device, leaseTime)
621 }
622
623 // Create the outgoing bufer and set the checksum in the packet
624 buff := gopacket.NewSerializeBuffer()
625 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530626 logger.Errorw(ctx, "Error in setting checksum", log.Fields{"Reason": err.Error()})
Naveen Sampath04696f72022-06-13 15:19:14 +0530627 return
628 }
629 opts := gopacket.SerializeOptions{
630 FixLengths: true,
631 ComputeChecksums: true,
632 }
633
634 cTagType := layers.EthernetTypeIPv4
635 eth.EthernetType = layers.EthernetTypeDot1Q
636
637 var pktLayers []gopacket.SerializableLayer
638 pktLayers = append(pktLayers, eth)
639
640 var qVlans []of.VlanType
641 var qVlanLayers []gopacket.SerializableLayer
642
643 if vpv.AllowTransparent {
644 vlanThreshold := 2
645 // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan
646 // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan
647 // Based on that, the no. of vlans should be ignored to get only transparent vlans
648 if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None {
649 vlanThreshold = 1
650 }
651 nxtLayer := layers.EthernetTypeDot1Q
652 if vlans := GetVlans(pkt); len(vlans) > vlanThreshold {
653 qVlans = vlans[vlanThreshold:]
654 cTagType = layers.EthernetTypeDot1Q
655 }
656 for i, qVlan := range qVlans {
657 vlan := uint16(qVlan)
658 if i == (len(qVlans) - 1) {
659 nxtLayer = layers.EthernetTypeIPv4
660 }
661 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
662 qVlanLayers = append(qVlanLayers, qdot1q)
663 }
664 }
665 switch vpv.VlanControl {
666 case ONUCVlanOLTSVlan:
667 cdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType}
668 pktLayers = append(pktLayers, cdot1q)
669 case ONUCVlan,
670 None:
671 sdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType}
672 pktLayers = append(pktLayers, sdot1q)
673 case OLTCVlanOLTSVlan,
674 OLTSVlan:
675 udot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType}
676 pktLayers = append(pktLayers, udot1q)
677 default:
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530678 logger.Warnw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
Naveen Sampath04696f72022-06-13 15:19:14 +0530679 }
680
681 pktLayers = append(pktLayers, qVlanLayers...)
682 pktLayers = append(pktLayers, ip)
683 pktLayers = append(pktLayers, udp)
684 pktLayers = append(pktLayers, dhcp4)
685 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
686 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
687 logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()})
688 return
689 }
690
691 if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil {
Akash Sonib3abf522022-12-19 13:20:02 +0530692 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error": err})
Naveen Sampath04696f72022-06-13 15:19:14 +0530693 }
694 }
695}
696
697// raiseDHCPv4Indication process DHCPv4 packet and raise indication
698func raiseDHCPv4Indication(msgType layers.DHCPMsgType, vpv *VoltPortVnet, smac net.HardwareAddr,
699 ip net.IP, pktPbit uint8, device string, leaseTime int64) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530700 logger.Debugw(ctx, "Processing Dhcpv4 packet", log.Fields{"ethsrcMac": smac.String(),
701 "MacLearningInVPV": vpv.MacLearning, "MacConfigured": vpv.MacAddr, "dhcpType": msgType,
702 "vlanPriority": pktPbit, "VPVLearntMac": vpv.LearntMacAddr})
703
704 matchServiceAndRaiseInd := func(key, value interface{}) bool {
705 // walk through all svcs under vpv and match pbit with packet.
706 svc := value.(*VoltService)
707
708 if svc.IsPbitExist(of.PbitType(pktPbit)) {
709 logger.Debugw(ctx, "Matching Pbit found in service config", log.Fields{"ServiceName": svc.Name, "Pbit": pktPbit})
710 return false
711 }
712 return true
713 }
714
715 switch msgType {
mgoudabb017dc2025-10-29 19:53:34 +0530716 case layers.DHCPMsgTypeDiscover:
717 vpv.SetDhcpState(DhcpRelayStateDiscover)
718 case layers.DHCPMsgTypeRequest:
719 vpv.SetDhcpState(DhcpRelayStateRequest)
Naveen Sampath04696f72022-06-13 15:19:14 +0530720 case layers.DHCPMsgTypeRelease:
721 vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
722 vpv.services.Range(matchServiceAndRaiseInd)
723 vpv.SetDhcpState(DhcpRelayStateRelease)
mgoudabb017dc2025-10-29 19:53:34 +0530724 case layers.DHCPMsgTypeAck:
Naveen Sampath04696f72022-06-13 15:19:14 +0530725 vpv.services.Range(matchServiceAndRaiseInd)
mgoudabb017dc2025-10-29 19:53:34 +0530726 vpv.SetDhcpState(DhcpRelayStateAck)
727 case layers.DHCPMsgTypeNak:
728 vpv.services.Range(matchServiceAndRaiseInd)
729 vpv.SetDhcpState(DhcpRelayStateNAK)
Naveen Sampath04696f72022-06-13 15:19:14 +0530730 case layers.DHCPMsgTypeOffer:
731 vpv.SetDhcpState(DhcpRelayStateOffer)
732 }
733}
734
735// raiseDHCPv6Indication process DHCPv6 packet and raise indication
736func raiseDHCPv6Indication(msgType layers.DHCPv6MsgType, vpv *VoltPortVnet,
737 smac net.HardwareAddr, ip net.IP, pktPbit uint8, device string, leaseTime uint32) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530738 logger.Debugw(ctx, "Processing DHCPv6 packet", log.Fields{"dhcpType": msgType,
739 "vlanPriority": pktPbit, "dhcpClientMac": smac.String(),
740 "MacLearningInVPV": vpv.MacLearning, "MacConfigured": vpv.MacAddr,
741 "VPVLearntMac": vpv.LearntMacAddr})
742
743 matchServiceAndRaiseInd := func(key, value interface{}) bool {
744 svc := value.(*VoltService)
745 if svc.IsPbitExist(of.PbitType(pktPbit)) {
746 logger.Debugw(ctx, "Matching Pbit found in service config", log.Fields{"ServiceName": svc.Name, "Pbit": pktPbit})
747 return false
748 }
749 return true
750 }
751
752 switch msgType {
753 case layers.DHCPv6MsgTypeSolicit:
754 vpv.SetDhcpv6State(Dhcpv6RelayStateSolicit)
755 // Reset learnt mac address in case of DHCPv6 release
756 case layers.DHCPv6MsgTypeRelease:
757 vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
758 vpv.services.Range(matchServiceAndRaiseInd)
759 vpv.SetDhcpv6State(Dhcpv6RelayStateRelease)
760
761 case layers.DHCPv6MsgTypeReply:
762 vpv.services.Range(matchServiceAndRaiseInd)
763 vpv.SetDhcpv6State(Dhcpv6RelayStateReply)
764 }
765}
766
767// ProcessUsDhcpv4Packet : The US DHCPv4 packet is identified the DHCP OP in the packet. A request is considered upstream
768// and the service associated with the packet is located by the port and VLANs in the packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530769func (va *VoltApplication) ProcessUsDhcpv4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530770 logger.Infow(ctx, "Processing Southbound US DHCPv4 packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +0530771 // We received the packet on an access port and the service for the packet can be
772 // gotten from the port and the packet
773 vpv, svc := va.GetVnetFromPkt(device, port, pkt)
774 if vpv == nil {
775 logger.Warn(ctx, "VNET couldn't be found from packet")
776 return
777 }
778
779 outport, _ := va.GetNniPort(device)
780 if outport == "" || outport == "0" {
781 logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport})
782 return
783 }
784
785 // Extract the layers in the packet to prepare the outgoing packet
786 // We use the layers to build the outgoing packet from scratch as
787 // the packet received can't be modified to add/remove layers
788 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
789 ip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
790 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
791 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
792 msgType := DhcpMsgType(dhcp4)
Naveen Sampath04696f72022-06-13 15:19:14 +0530793
794 // Learn the 8021P values from the packet received
795 var priority uint8
796 var dropEligible bool
797 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
798 if dot1ql != nil {
799 dot1q := dot1ql.(*layers.Dot1Q)
800 priority = dot1q.Priority
801 dropEligible = dot1q.DropEligible
802 }
803 // If this is the first message in the DHCP sequence, the service
804 // is added to the DHCP relay application. The reply packets locate
805 // the associated service/session from the relay application.
806 if msgType == layers.DHCPMsgTypeDiscover || msgType == layers.DHCPMsgTypeRequest {
807 if err := dhcpNws.AddDhcpSession(pkt, vpv); err != nil {
808 logger.Errorw(ctx, "Adding dhcp session failed", log.Fields{"Error": err})
809 }
810 }
811
812 // Raise mac-learnt(DHCP Discover) indication when mac learning is enabled and learnt mac
813 // is not same as received mac address. If mac learning disabled, we have mac address in the
814 // service configuration. Hence mac learnt indication is not raised
815 // Reset learnt mac address in case of DHCP release and raise the indication
816 if vpv.DhcpRelay {
817 // If this is the first message in the DHCP sequence, the service
818 // is added to the DHCP relay application. The reply packets locate
819 // the associated service/session from the relay application.
820 // DS HSIA flows will be added after DHCP ACK .
821 if msgType == layers.DHCPMsgTypeDiscover || msgType == layers.DHCPMsgTypeRequest {
822 if !util.MacAddrsMatch(vpv.MacAddr, dhcp4.ClientHWAddr) {
823 // MAC is different and relearning is disabled.
824 if NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn {
825 // update learnt mac for debug purpose
826 vpv.LearntMacAddr = dhcp4.ClientHWAddr
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530827 vpv.WriteToDb(cntx)
Naveen Sampath04696f72022-06-13 15:19:14 +0530828 logger.Warnw(ctx, "Dropping the packet Mac relearn is disabled",
829 log.Fields{"vpv.MacAddr": vpv.MacAddr, "LearntMac": dhcp4.ClientHWAddr})
830 return
831 }
832 expectedPort := va.GetMacInPortMap(dhcp4.ClientHWAddr)
833 if expectedPort != "" && expectedPort != vpv.Port {
834 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()})
835 return
836 }
837 }
838 }
839 raiseDHCPv4Indication(msgType, vpv, dhcp4.ClientHWAddr, vpv.Ipv4Addr, priority, device, 0)
840
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +0530841 // Check IsOption82Enabled flag in configuration. if true(enabled), add option82 into dhcpv4 header.
Naveen Sampath04696f72022-06-13 15:19:14 +0530842 // Remote id can be custom or mac address.
843 // If remote id is custom, then add service will carry the remote id
844 // If remote id is mac address, and if mac is configured, then add service will carry the remote id
845 // If remote id is mac address, in mac learning case, then mac has to be taken from dhcp packet
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +0530846 if svc.IsOption82Enabled {
Naveen Sampath04696f72022-06-13 15:19:14 +0530847 var remoteID []byte
848 if svc.RemoteIDType == string(MACAddress) {
849 remoteID = []byte((dhcp4.ClientHWAddr).String())
850 } else if svc.RemoteID != nil {
851 remoteID = svc.RemoteID
852 }
853 AddDhcpv4Option82(svc, remoteID, dhcp4)
854 }
855 }
856
857 buff := gopacket.NewSerializeBuffer()
858 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
859 logger.Error(ctx, "Error in setting checksum")
860 return
861 }
862 opts := gopacket.SerializeOptions{
863 FixLengths: true,
864 ComputeChecksums: true,
865 }
866
867 cTagType := layers.EthernetTypeIPv4
868 outerVlan, innerVlan := vpv.GetNniVlans()
869 logger.Debugw(ctx, "Vnet Vlans", log.Fields{"Svlan": outerVlan, "Cvlan": innerVlan})
870 eth.EthernetType = vpv.SVlanTpid
871
872 var pktLayers []gopacket.SerializableLayer
873 pktLayers = append(pktLayers, eth)
874
875 var qVlans []of.VlanType
876 var qVlanLayers []gopacket.SerializableLayer
877
878 if vpv.AllowTransparent {
879 nxtLayer := layers.EthernetTypeDot1Q
880 if vlans := GetVlans(pkt); len(vlans) > 1 {
881 qVlans = vlans[1:]
882 logger.Debugw(ctx, "Q Vlans", log.Fields{"Vlan List": qVlans})
883 cTagType = layers.EthernetTypeDot1Q
884 }
885 for i, qVlan := range qVlans {
886 vlan := uint16(qVlan)
887 if i == (len(qVlans) - 1) {
888 nxtLayer = layers.EthernetTypeIPv4
889 }
890 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
891 qVlanLayers = append(qVlanLayers, qdot1q)
892 }
893 }
894 switch vpv.VlanControl {
895 case ONUCVlanOLTSVlan,
896 OLTCVlanOLTSVlan:
897 sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q}
898 pktLayers = append(pktLayers, sdot1q)
899 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType}
900 pktLayers = append(pktLayers, cdot1q)
901 case ONUCVlan,
902 OLTSVlan,
903 None:
904 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType}
905 pktLayers = append(pktLayers, cdot1q)
906 default:
907 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
908 }
909
910 pktLayers = append(pktLayers, qVlanLayers...)
911 pktLayers = append(pktLayers, ip)
912 pktLayers = append(pktLayers, udp)
913 pktLayers = append(pktLayers, dhcp4)
914 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
915 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
916 return
917 }
918
919 // Now the packet constructed is output towards the switch to be emitted on
920 // the NNI port
921 if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil {
Akash Sonib3abf522022-12-19 13:20:02 +0530922 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error": err})
Naveen Sampath04696f72022-06-13 15:19:14 +0530923 }
924 if vpv.DhcpRelay {
925 // Inform dhcp request information to dhcp server handler
926 dhcpRequestReceived(uint16(vpv.CVlan), uint16(vpv.SVlan), eth.SrcMAC.String())
927 }
928}
929
930// ProcessUDP4Packet : CallBack function registered with application to handle DHCP packetIn
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530931func ProcessUDP4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
932 GetApplication().ProcessUDP4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530933}
934
935// ProcessUDP4Packet : The packet is a UDP packet and currently only DHCP relay application is supported
936// We determine the packet direction and process it based on the direction
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530937func (va *VoltApplication) ProcessUDP4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530938 // Currently DHCP is the only application supported by the application
vinokuma926cb3e2023-03-29 11:41:06 +0530939 // We check for DHCP before proceeding further. In future, this could be
Naveen Sampath04696f72022-06-13 15:19:14 +0530940 // based on registration and the callbacks
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530941 logger.Debugw(ctx, "Process UDP4 Packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +0530942 dhcpl := pkt.Layer(layers.LayerTypeDHCPv4)
943 if dhcpl == nil {
944 return
945 }
946 //logger.Debugw(ctx, "Received Packet In", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
947 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
948 if dhcp4.Operation == layers.DHCPOpRequest {
949 // This is treated as an upstream packet in the VOLT application
950 // as VOLT serves access subscribers who use DHCP to acquire IP
951 // address and these packets go upstream to the network
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530952 va.ProcessUsDhcpv4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530953 } else {
954 // This is a downstream packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530955 va.ProcessDsDhcpv4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530956 }
Naveen Sampath04696f72022-06-13 15:19:14 +0530957}
958
959// ProcessUDP6Packet : CallBack function registered with application to handle DHCPv6 packetIn
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530960func ProcessUDP6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
961 GetApplication().ProcessUDP6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530962}
963
964// ProcessUDP6Packet : As a LDRA node, we expect to see only RelayReply from the DHCP server and we always
965// pack the received request and send it to the server as a RelayForward message
966// We expect to see Solicit, Request in the most normal cases. Before the lease expires
967// we should also see Renew. However, we should always pack the US message by adding
968// additional option that identifies to the server that the DHCP packet is forwarded
969// by an LDRA node.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530970func (va *VoltApplication) ProcessUDP6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) []byte {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530971 logger.Debugw(ctx, "Processing DHCPv6 packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +0530972 dhcpl := pkt.Layer(layers.LayerTypeDHCPv6)
973 if dhcpl == nil {
974 return nil
975 }
Naveen Sampath04696f72022-06-13 15:19:14 +0530976 dhcpv6 := dhcpl.(*layers.DHCPv6)
977 switch dhcpv6.MsgType {
978 case layers.DHCPv6MsgTypeSolicit, layers.DHCPv6MsgTypeRequest, layers.DHCPv6MsgTypeRenew,
979 layers.DHCPv6MsgTypeRelease, layers.DHCPv6MsgTypeRebind, layers.DHCPv6MsgTypeInformationRequest,
980 layers.DHCPv6MsgTypeDecline:
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530981 va.ProcessUsDhcpv6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530982 case layers.DHCPv6MsgTypeAdvertise, layers.DHCPv6MsgTypeConfirm, layers.DHCPv6MsgTypeReconfigure:
983 logger.Warnw(ctx, "SouthBound DHCPv6 DS Messages Expected For a Relay Agent", log.Fields{"Type": dhcpv6.MsgType})
984 case layers.DHCPv6MsgTypeRelayForward:
985 logger.Warn(ctx, "As the first DHCPv6 Relay Agent, Unexpected Relay Forward")
986 case layers.DHCPv6MsgTypeRelayReply:
987 // We received a response from the server
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530988 va.ProcessDsDhcpv6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530989 }
990 return nil
991}
992
993// GetRelayReplyBytes to get relay reply bytes
994func GetRelayReplyBytes(dhcp6 *layers.DHCPv6) []byte {
995 for _, o := range dhcp6.Options {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530996 logger.Debugw(ctx, "Received Option", log.Fields{"Code": o.Code})
Naveen Sampath04696f72022-06-13 15:19:14 +0530997 if o.Code == layers.DHCPv6OptRelayMessage {
998 return o.Data
999 }
1000 }
1001 return nil
1002}
1003
1004// BuildRelayFwd to build forward relay
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301005func BuildRelayFwd(paddr net.IP, intfID []byte, remoteID []byte, payload []byte, isOption82Enabled bool, dhcpRelay bool) *layers.DHCPv6 {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301006 logger.Debugw(ctx, "Build Relay Fwd", log.Fields{"Paddr": paddr, "isOption82Enabled": isOption82Enabled, "dhcpRelay": dhcpRelay})
Naveen Sampath04696f72022-06-13 15:19:14 +05301007 dhcp6 := &layers.DHCPv6{MsgType: layers.DHCPv6MsgTypeRelayForward, LinkAddr: net.ParseIP("::"), PeerAddr: []byte(paddr)}
1008 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptRelayMessage, payload))
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301009 // Check IsOption82Enabled flag in configuration. if true(enabled), add remoteID and circuitID into dhcpv6 header.
Naveen Sampath04696f72022-06-13 15:19:14 +05301010 if dhcpRelay {
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301011 if isOption82Enabled {
Naveen Sampath04696f72022-06-13 15:19:14 +05301012 remote := &layers.DHCPv6RemoteId{RemoteId: remoteID}
1013 if len(remoteID) != 0 {
1014 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptRemoteID, remote.Encode()))
1015 }
1016 if len(intfID) != 0 {
1017 intf := &layers.DHCPv6IntfId{Data: intfID}
1018 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptInterfaceID, intf.Encode()))
1019 }
1020 }
1021 }
1022 return dhcp6
1023}
1024
vinokuma926cb3e2023-03-29 11:41:06 +05301025// nolint: gocyclo
Naveen Sampath04696f72022-06-13 15:19:14 +05301026// ProcessUsDhcpv6Packet to rpocess upstream DHCPv6 packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301027func (va *VoltApplication) ProcessUsDhcpv6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +05301028 // We received the packet on an access port and the service for the packet can be
1029 // gotten from the port and the packet
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301030 logger.Infow(ctx, "Processing Southbound US DHCPv6 packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +05301031 logger.Debugw(ctx, "Packet IN", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
1032 vpv, svc := va.GetVnetFromPkt(device, port, pkt)
1033 if vpv == nil {
1034 logger.Warn(ctx, "VNET couldn't be found from packet")
1035 return
1036 }
1037
1038 outport, _ := va.GetNniPort(device)
1039 if outport == "" || outport == "0" {
1040 logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport})
1041 return
1042 }
1043
1044 // Extract the layers in the packet to prepare the outgoing packet
1045 // We use the layers to build the outgoing packet from scratch as
1046 // the packet received can't be modified to add/remove layers
1047 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
1048 ip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
1049 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
1050 idhcp6 := pkt.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
1051
1052 // Remote id can be custom or mac address.
1053 // If remote id is custom, then add service will carry the remote id
1054 // If remote id is mac address, and if mac is configured, then add service will carry the remote id
1055 // If remote id is mac address, in mac learning case, then mac has to be taken from dhcp packet
1056 var remoteID []byte
1057 if svc.RemoteIDType == string(MACAddress) {
1058 remoteID = []byte((eth.SrcMAC).String())
1059 } else if svc.RemoteID != nil {
1060 remoteID = svc.RemoteID
1061 }
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301062 dhcp6 := BuildRelayFwd(ip.SrcIP, svc.GetCircuitID(), remoteID, udp.Payload, svc.IsOption82Enabled, vpv.DhcpRelay)
Naveen Sampath04696f72022-06-13 15:19:14 +05301063
1064 var sourceMac = eth.SrcMAC
1065 var sessionKey [MaxLenDhcpv6DUID]byte
1066
1067 clientDuid, decodedDuid := getDhcpv6ClientDUID(idhcp6)
1068 if clientDuid == nil || decodedDuid == nil {
1069 copy(sessionKey[:], eth.SrcMAC)
1070 } else {
1071 copy(sessionKey[:], clientDuid[0:])
1072 if decodedDuid.Type == layers.DHCPv6DUIDTypeLLT || decodedDuid.Type == layers.DHCPv6DUIDTypeLL {
1073 sourceMac = decodedDuid.LinkLayerAddress
1074 }
1075 }
1076 // Learn the 8021P values from the packet received
1077 var priority uint8
1078 var dropEligible bool
1079 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
1080 if dot1ql != nil {
1081 dot1q := dot1ql.(*layers.Dot1Q)
1082 priority = dot1q.Priority
1083 dropEligible = dot1q.DropEligible
1084 }
1085 if idhcp6.MsgType == layers.DHCPv6MsgTypeSolicit {
1086 if err := dhcpNws.AddDhcp6Session(sessionKey, vpv); err != nil {
1087 logger.Errorw(ctx, "Adding dhcpv6 session failed", log.Fields{"Error": err})
1088 }
1089 vpv.DHCPv6DUID = sessionKey
1090 }
1091
1092 // Raise mac-learnt(DHCPv6MsgTypeSolicit) indication when mac learning is enabled and learnt mac
1093 // is not same as received mac address. If mac learning disabled, we have mac address in the
1094 // service configuration. Hence mac learnt indication is not raised
1095 if vpv.DhcpRelay {
1096 if idhcp6.MsgType == layers.DHCPv6MsgTypeSolicit {
1097 if !util.MacAddrsMatch(vpv.MacAddr, sourceMac) {
1098 // MAC is different and relearning is disabled.
1099 if NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn {
1100 // update learnt mac for debug purpose
1101 vpv.LearntMacAddr = sourceMac
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301102 vpv.WriteToDb(cntx)
Naveen Sampath04696f72022-06-13 15:19:14 +05301103 logger.Warnw(ctx, "Dropping the packet Mac relearn is disabled",
1104 log.Fields{"vpv.MacAddr": vpv.MacAddr, "LearntMac": sourceMac})
1105 return
1106 }
1107 expectedPort := va.GetMacInPortMap(sourceMac)
1108 if expectedPort != "" && expectedPort != vpv.Port {
1109 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()})
1110 return
1111 }
1112 }
1113 }
1114 raiseDHCPv6Indication(idhcp6.MsgType, vpv, sourceMac, vpv.Ipv6Addr, priority, device, 0)
1115 }
1116
1117 // Create the buffer and the encode options for the outgoing packet
1118 buff := gopacket.NewSerializeBuffer()
1119 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
1120 logger.Error(ctx, "Error in setting checksum")
1121 return
1122 }
1123 opts := gopacket.SerializeOptions{
1124 FixLengths: true,
1125 ComputeChecksums: true,
1126 }
1127
1128 cTagType := layers.EthernetTypeIPv6
1129 outerVlan, innerVlan := vpv.GetNniVlans()
1130 eth.EthernetType = vpv.SVlanTpid
1131
1132 var pktLayers []gopacket.SerializableLayer
1133 pktLayers = append(pktLayers, eth)
1134
1135 var qVlans []of.VlanType
1136 var qVlanLayers []gopacket.SerializableLayer
1137
1138 if vpv.AllowTransparent {
1139 nxtLayer := layers.EthernetTypeDot1Q
1140 if vlans := GetVlans(pkt); len(vlans) > 1 {
1141 qVlans = vlans[1:]
1142 cTagType = layers.EthernetTypeDot1Q
1143 }
1144 for i, qVlan := range qVlans {
1145 vlan := uint16(qVlan)
1146 if i == (len(qVlans) - 1) {
1147 nxtLayer = layers.EthernetTypeIPv6
1148 }
1149 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
1150 qVlanLayers = append(qVlanLayers, qdot1q)
1151 }
Naveen Sampath04696f72022-06-13 15:19:14 +05301152 }
1153 switch vpv.VlanControl {
1154 case ONUCVlanOLTSVlan,
1155 OLTCVlanOLTSVlan:
1156 sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q}
1157 pktLayers = append(pktLayers, sdot1q)
1158 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType}
1159 pktLayers = append(pktLayers, cdot1q)
1160 case ONUCVlan,
1161 OLTSVlan,
1162 None:
1163 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType}
1164 pktLayers = append(pktLayers, cdot1q)
1165 default:
1166 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
1167 }
1168
1169 pktLayers = append(pktLayers, qVlanLayers...)
1170 pktLayers = append(pktLayers, ip)
1171 pktLayers = append(pktLayers, udp)
1172 pktLayers = append(pktLayers, dhcp6)
1173 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
1174 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
1175 return
1176 }
1177 // Now the packet constructed is output towards the switch to be emitted on
1178 // the NNI port
1179 if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil {
Akash Sonib3abf522022-12-19 13:20:02 +05301180 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error": err})
Naveen Sampath04696f72022-06-13 15:19:14 +05301181 }
1182 if vpv.DhcpRelay {
1183 // Inform dhcp request information to dhcp server handler
1184 dhcpRequestReceived(uint16(vpv.CVlan), uint16(vpv.SVlan), eth.SrcMAC.String())
1185 }
1186}
1187
1188// GetDhcpv6 to get dhcpv6 info
1189func GetDhcpv6(payload []byte) (*layers.DHCPv6, error) {
1190 pkt := gopacket.NewPacket(payload, layers.LayerTypeDHCPv6, gopacket.Default)
1191 if dl := pkt.Layer(layers.LayerTypeDHCPv6); dl != nil {
1192 if dhcp6, ok := dl.(*layers.DHCPv6); ok {
1193 return dhcp6, nil
1194 }
1195 }
1196 return nil, errors.New("Failed to decode DHCPv6")
1197}
1198
1199// ProcessDsDhcpv6Packet to process downstream dhcpv6 packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301200func (va *VoltApplication) ProcessDsDhcpv6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +05301201 logger.Infow(ctx, "Processing Southbound DS DHCPv6 packet", log.Fields{"Port": port})
1202 logger.Debugw(ctx, "Packet IN", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
1203
1204 // Retrieve the layers to build the outgoing packet. It is not
1205 // possible to add/remove layers to the existing packet and thus
1206 // the lyayers are extracted to build the outgoing packet
1207 // The DHCP layer is handled differently. The Relay-Reply option
1208 // of DHCP is extracted and is made the UDP payload.
1209 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
1210 ip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
1211 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
1212 idhcp6 := pkt.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
1213 //var dhcp6 *layers.DHCPv6
1214 var payload []byte
1215 if payload = GetRelayReplyBytes(idhcp6); payload == nil {
1216 logger.Warn(ctx, "Didn't Receive RelayMessage IE")
1217 return
1218 }
1219
1220 dhcp6, err := GetDhcpv6(payload)
1221 if err != nil {
1222 logger.Warnw(ctx, "DHCPv6 Decode Failed", log.Fields{"Reason": err.Error()})
1223 return
1224 }
1225
1226 // Learn the 8021P values from the packet received
1227 var priority uint8
1228 var dsPbit uint8
1229 var dropEligible bool
1230 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
1231 if dot1ql != nil {
1232 dot1q := dot1ql.(*layers.Dot1Q)
1233 priority = dot1q.Priority
1234 dropEligible = dot1q.DropEligible
1235 }
1236
1237 pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt)
1238 vpvList, clientMac, err := GetVnetForV6Nni(dhcp6, pktInnerlan, pktOuterlan, priority, eth.DstMAC)
1239 if len(vpvList) == 0 {
1240 logger.Warnw(ctx, "VNET couldn't be found for NNI", log.Fields{"Reason": err})
1241 return
1242 }
1243
1244 ipv6Addr, leaseTime := GetIpv6Addr(dhcp6)
1245
1246 for _, vpv := range vpvList {
Naveen Sampath04696f72022-06-13 15:19:14 +05301247 dsPbit = vpv.GetRemarkedPriority(priority)
1248 // Raise DHCPv6 Reply indication
1249 if vpv.DhcpRelay {
1250 // Inform dhcp response information to dhcp server handler
1251 dhcpResponseReceived(uint16(vpv.CVlan), uint16(vpv.SVlan))
1252
1253 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply && ipv6Addr != nil {
1254 // separate go rotuine is spawned to avoid drop of ACK packet
1255 // as HSIA flows will be deleted if new MAC is learnt.
1256 if len(vpvList) == 1 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301257 go vpv.SetMacAddr(cntx, clientMac)
Naveen Sampath04696f72022-06-13 15:19:14 +05301258 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301259 vpv.Dhcpv6ResultInd(cntx, ipv6Addr, leaseTime)
Naveen Sampath04696f72022-06-13 15:19:14 +05301260 }
1261 raiseDHCPv6Indication(dhcp6.MsgType, vpv, clientMac, ipv6Addr, dsPbit, device, leaseTime)
1262 }
1263
1264 //Replace dst Port value to 546
1265 udp.DstPort = 546
1266 logger.Infow(ctx, "Packet Out UDP Port..", log.Fields{"UDP": udp, "Port": udp.DstPort})
1267
1268 // Create the buffer and the encode options for the outgoing packet
1269 buff := gopacket.NewSerializeBuffer()
1270 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
1271 logger.Error(ctx, "Error in setting checksum")
1272 return
1273 }
1274 opts := gopacket.SerializeOptions{
1275 FixLengths: true,
1276 ComputeChecksums: true,
1277 }
1278
1279 cTagType := layers.EthernetTypeIPv6
1280 eth.EthernetType = layers.EthernetTypeDot1Q
1281
1282 var pktLayers []gopacket.SerializableLayer
1283 pktLayers = append(pktLayers, eth)
1284
1285 var qVlans []of.VlanType
1286 var qVlanLayers []gopacket.SerializableLayer
1287
1288 if vpv.AllowTransparent {
1289 vlanThreshold := 2
1290 // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan
1291 // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan
1292 // Based on that, the no. of vlans should be ignored to get only transparent vlans
1293 if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None {
1294 vlanThreshold = 1
1295 }
1296 nxtLayer := layers.EthernetTypeDot1Q
1297 if vlans := GetVlans(pkt); len(vlans) > vlanThreshold {
1298 qVlans = vlans[vlanThreshold:]
1299 cTagType = layers.EthernetTypeDot1Q
1300 }
1301 for i, qVlan := range qVlans {
1302 vlan := uint16(qVlan)
1303 if i == (len(qVlans) - 1) {
1304 nxtLayer = layers.EthernetTypeIPv6
1305 }
1306 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
1307 qVlanLayers = append(qVlanLayers, qdot1q)
1308 }
Naveen Sampath04696f72022-06-13 15:19:14 +05301309 }
1310 switch vpv.VlanControl {
1311 case ONUCVlanOLTSVlan:
1312 cdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType}
1313 pktLayers = append(pktLayers, cdot1q)
1314 case ONUCVlan,
1315 None:
1316 sdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType}
1317 pktLayers = append(pktLayers, sdot1q)
1318 case OLTCVlanOLTSVlan,
1319 OLTSVlan:
1320 udot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType}
1321 pktLayers = append(pktLayers, udot1q)
1322 default:
1323 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
1324 }
1325
1326 pktLayers = append(pktLayers, qVlanLayers...)
1327 pktLayers = append(pktLayers, ip)
1328 pktLayers = append(pktLayers, udp)
1329 pktLayers = append(pktLayers, dhcp6)
1330 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
1331 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
1332 logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()})
1333 return
1334 }
1335
1336 if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil {
1337 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Reason": err.Error()})
1338 }
1339 }
1340}
1341
1342// The DHCP relay application is maintained within the structures below
1343var dhcpNws *DhcpNetworks
1344
1345func init() {
1346 dhcpNws = NewDhcpNetworks()
1347}
Tinoj Josephec742f62022-09-29 19:11:10 +05301348
1349type DhcpAllocation struct {
Akash Sonib3abf522022-12-19 13:20:02 +05301350 SubscriberID string `json:"subscriberId"`
1351 ConnectPoint string `json:"connectPoint"`
vinokuma926cb3e2023-03-29 11:41:06 +05301352 AllocationTimeStamp time.Time `json:"allocationTimestamp"`
Akash Sonib3abf522022-12-19 13:20:02 +05301353 MacAddress net.HardwareAddr `json:"macAddress"`
vinokuma926cb3e2023-03-29 11:41:06 +05301354 CircuitID []byte `json:"circuitId"`
1355 IPAllocated net.IP `json:"ipAllocated"`
Akash Sonib3abf522022-12-19 13:20:02 +05301356 State int `json:"state"`
1357 VlanID int `json:"vlanId"`
Tinoj Josephec742f62022-09-29 19:11:10 +05301358}
1359
1360// GetAllocations returns DhcpAllocation info for all devices or for a device ID
Akash Sonib3abf522022-12-19 13:20:02 +05301361func (va *VoltApplication) GetAllocations(cntx context.Context, deviceID string) ([]DhcpAllocation, error) {
Tinoj Josephec742f62022-09-29 19:11:10 +05301362 logger.Debugw(ctx, "GetAllocations", log.Fields{"DeviceID": deviceID})
Akash Sonib3abf522022-12-19 13:20:02 +05301363 allocations := []DhcpAllocation{}
Tinoj Josephec742f62022-09-29 19:11:10 +05301364 for _, drv := range dhcpNws.Networks {
1365 drv.sessionLock.RLock()
1366 for _, session := range drv.sessions {
1367 vpv, ok := session.(*VoltPortVnet)
1368 if ok {
1369 var subscriber string
1370 // return Name of first service
1371 vpv.services.Range(func(key, value interface{}) bool {
1372 svc := value.(*VoltService)
1373 subscriber = svc.Name
1374 return false
1375 })
1376 // If deviceID is not provided, return all allocations
1377 // If deviceID exists then filter on deviceID
1378 if len(deviceID) == 0 || deviceID == vpv.Device {
Akash Sonib3abf522022-12-19 13:20:02 +05301379 allocation := DhcpAllocation{
1380 SubscriberID: subscriber,
1381 ConnectPoint: vpv.Device,
1382 MacAddress: vpv.MacAddr,
1383 State: int(vpv.RelayState),
1384 VlanID: int(vpv.SVlan),
1385 CircuitID: vpv.CircuitID,
vinokuma926cb3e2023-03-29 11:41:06 +05301386 IPAllocated: vpv.Ipv4Addr,
Akash Sonib3abf522022-12-19 13:20:02 +05301387 AllocationTimeStamp: vpv.DhcpExpiryTime,
1388 }
Tinoj Josephec742f62022-09-29 19:11:10 +05301389 logger.Debugw(ctx, "DHCP Allocation found", log.Fields{"DhcpAlloc": allocation})
1390 allocations = append(allocations, allocation)
1391 }
1392 }
1393 }
1394 drv.sessionLock.RUnlock()
1395 }
1396 return allocations, nil
1397}
Akash Sonib3abf522022-12-19 13:20:02 +05301398
1399type MacLearnerInfo struct {
vinokuma926cb3e2023-03-29 11:41:06 +05301400 DeviceID string `json:"deviceId"`
Akash Sonib3abf522022-12-19 13:20:02 +05301401 PortNumber string `json:"portNumber"`
vinokuma926cb3e2023-03-29 11:41:06 +05301402 VlanID string `json:"vlanId"`
Akash Sonib3abf522022-12-19 13:20:02 +05301403 MacAddress string `json:"macAddress"`
1404}
1405
1406func (va *VoltApplication) GetAllMacLearnerInfo() ([]MacLearnerInfo, error) {
1407 logger.Info(ctx, "GetMacLearnerInfo")
1408 macLearner := []MacLearnerInfo{}
1409 for _, drv := range dhcpNws.Networks {
1410 logger.Debugw(ctx, "drv found", log.Fields{"drv": drv})
1411 drv.sessionLock.RLock()
1412 for _, session := range drv.sessions {
1413 vpv, ok := session.(*VoltPortVnet)
1414 if ok {
1415 macLearn := MacLearnerInfo{
vinokuma926cb3e2023-03-29 11:41:06 +05301416 DeviceID: vpv.Device,
Akash Sonib3abf522022-12-19 13:20:02 +05301417 PortNumber: vpv.Port,
vinokuma926cb3e2023-03-29 11:41:06 +05301418 VlanID: vpv.SVlan.String(),
Akash Sonib3abf522022-12-19 13:20:02 +05301419 MacAddress: vpv.MacAddr.String(),
1420 }
1421 logger.Debugw(ctx, "MacLerner found", log.Fields{"MacLearn": macLearn})
1422 macLearner = append(macLearner, macLearn)
1423 }
1424 }
1425 drv.sessionLock.RUnlock()
1426 }
1427 return macLearner, nil
1428}
1429
vinokuma926cb3e2023-03-29 11:41:06 +05301430func (va *VoltApplication) GetMacLearnerInfo(cntx context.Context, deviceID, portNumber, vlanID string) (MacLearnerInfo, error) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301431 logger.Debugw(ctx, "GetMecLearnerInfo", log.Fields{"DeviceID": deviceID, "PortNumber": portNumber, "VlanID": vlanID})
Akash Sonib3abf522022-12-19 13:20:02 +05301432 macLearn := MacLearnerInfo{}
1433 for _, drv := range dhcpNws.Networks {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301434 logger.Debugw(ctx, "drv found", log.Fields{"drv": drv})
Akash Sonib3abf522022-12-19 13:20:02 +05301435 drv.sessionLock.RLock()
1436 for _, session := range drv.sessions {
1437 vpv, ok := session.(*VoltPortVnet)
1438 if ok {
vinokuma926cb3e2023-03-29 11:41:06 +05301439 if deviceID == vpv.Device && portNumber == vpv.Port && vlanID == vpv.SVlan.String() {
Akash Sonib3abf522022-12-19 13:20:02 +05301440 macLearn = MacLearnerInfo{
vinokuma926cb3e2023-03-29 11:41:06 +05301441 DeviceID: vpv.Device,
Akash Sonib3abf522022-12-19 13:20:02 +05301442 PortNumber: vpv.Port,
vinokuma926cb3e2023-03-29 11:41:06 +05301443 VlanID: vpv.SVlan.String(),
Akash Sonib3abf522022-12-19 13:20:02 +05301444 MacAddress: vpv.MacAddr.String(),
1445 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301446 logger.Debugw(ctx, "MacLerner found", log.Fields{"MacLearn": macLearn})
vinokuma926cb3e2023-03-29 11:41:06 +05301447 } else if deviceID == vpv.Device && portNumber == vpv.Port && vlanID == "" {
Akash Sonib3abf522022-12-19 13:20:02 +05301448 macLearn = MacLearnerInfo{
vinokuma926cb3e2023-03-29 11:41:06 +05301449 DeviceID: vpv.Device,
Akash Sonib3abf522022-12-19 13:20:02 +05301450 PortNumber: vpv.Port,
vinokuma926cb3e2023-03-29 11:41:06 +05301451 VlanID: vpv.SVlan.String(),
Akash Sonib3abf522022-12-19 13:20:02 +05301452 MacAddress: vpv.MacAddr.String(),
1453 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301454 logger.Debugw(ctx, "MacLerner found", log.Fields{"MacLearn": macLearn})
Akash Sonib3abf522022-12-19 13:20:02 +05301455 }
1456 }
1457 }
1458 drv.sessionLock.RUnlock()
1459 }
1460 return macLearn, nil
1461}
1462
1463func (va *VoltApplication) GetIgnoredPorts() (map[string][]string, error) {
1464 logger.Info(ctx, "GetIgnoredPorts")
1465 IgnoredPorts := make(map[string][]string)
1466 portIgnored := func(key, value interface{}) bool {
1467 voltDevice := value.(*VoltDevice)
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301468 logger.Debugw(ctx, "Inside GetIgnoredPorts method", log.Fields{"deviceName": voltDevice.Name})
Akash Sonib3abf522022-12-19 13:20:02 +05301469 voltDevice.Ports.Range(func(key, value interface{}) bool {
1470 port := key.(string)
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301471 logger.Debugw(ctx, "Inside GetIgnoredPorts method", log.Fields{"port": port})
Akash Sonib3abf522022-12-19 13:20:02 +05301472 //Obtain all VPVs associated with the port
1473 vnets, ok := GetApplication().VnetsByPort.Load(port)
1474 if !ok {
1475 return true
1476 }
1477 for _, vpv := range vnets.([]*VoltPortVnet) {
Akash Sonib3abf522022-12-19 13:20:02 +05301478 if vpv.MacLearning == MacLearningNone {
1479 IgnoredPorts[vpv.Device] = append(IgnoredPorts[vpv.Device], vpv.Port)
1480 }
1481 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301482 logger.Warnw(ctx, "Ignored Port", log.Fields{"Ignored Port": IgnoredPorts})
Akash Sonib3abf522022-12-19 13:20:02 +05301483 return true
1484 })
1485 return true
1486 }
1487 va.DevicesDisc.Range(portIgnored)
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301488 logger.Debug(ctx, "GetIgnoredPorts completed")
Akash Sonib3abf522022-12-19 13:20:02 +05301489 return IgnoredPorts, nil
1490}