blob: 354db3cca45db9902633fc453b646c92cee63a1b [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()
222 logger.Infow(ctx, "DHCP Sessions deleted", log.Fields{"MAC": addr})
223}
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 {
392 logger.Infow(ctx, "ClientIdentifier", log.Fields{"DUID": duid, "Option": option.String()})
393 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 {
716 case layers.DHCPMsgTypeDiscover, layers.DHCPMsgTypeRequest:
717 if msgType == layers.DHCPMsgTypeDiscover {
718 vpv.SetDhcpState(DhcpRelayStateDiscover)
719 } else if msgType == layers.DHCPMsgTypeRequest {
720 vpv.SetDhcpState(DhcpRelayStateRequest)
721 }
722 // Reset learnt mac address in case of DHCPv4 release
723 case layers.DHCPMsgTypeRelease:
724 vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
725 vpv.services.Range(matchServiceAndRaiseInd)
726 vpv.SetDhcpState(DhcpRelayStateRelease)
727
728 case layers.DHCPMsgTypeAck, layers.DHCPMsgTypeNak:
729 vpv.services.Range(matchServiceAndRaiseInd)
730 if msgType == layers.DHCPMsgTypeAck {
731 vpv.SetDhcpState(DhcpRelayStateAck)
732 } else if msgType == layers.DHCPMsgTypeNak {
733 vpv.SetDhcpState(DhcpRelayStateNAK)
734 }
735 case layers.DHCPMsgTypeOffer:
736 vpv.SetDhcpState(DhcpRelayStateOffer)
737 }
738}
739
740// raiseDHCPv6Indication process DHCPv6 packet and raise indication
741func raiseDHCPv6Indication(msgType layers.DHCPv6MsgType, vpv *VoltPortVnet,
742 smac net.HardwareAddr, ip net.IP, pktPbit uint8, device string, leaseTime uint32) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530743 logger.Debugw(ctx, "Processing DHCPv6 packet", log.Fields{"dhcpType": msgType,
744 "vlanPriority": pktPbit, "dhcpClientMac": smac.String(),
745 "MacLearningInVPV": vpv.MacLearning, "MacConfigured": vpv.MacAddr,
746 "VPVLearntMac": vpv.LearntMacAddr})
747
748 matchServiceAndRaiseInd := func(key, value interface{}) bool {
749 svc := value.(*VoltService)
750 if svc.IsPbitExist(of.PbitType(pktPbit)) {
751 logger.Debugw(ctx, "Matching Pbit found in service config", log.Fields{"ServiceName": svc.Name, "Pbit": pktPbit})
752 return false
753 }
754 return true
755 }
756
757 switch msgType {
758 case layers.DHCPv6MsgTypeSolicit:
759 vpv.SetDhcpv6State(Dhcpv6RelayStateSolicit)
760 // Reset learnt mac address in case of DHCPv6 release
761 case layers.DHCPv6MsgTypeRelease:
762 vpv.LearntMacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
763 vpv.services.Range(matchServiceAndRaiseInd)
764 vpv.SetDhcpv6State(Dhcpv6RelayStateRelease)
765
766 case layers.DHCPv6MsgTypeReply:
767 vpv.services.Range(matchServiceAndRaiseInd)
768 vpv.SetDhcpv6State(Dhcpv6RelayStateReply)
769 }
770}
771
772// ProcessUsDhcpv4Packet : The US DHCPv4 packet is identified the DHCP OP in the packet. A request is considered upstream
773// and the service associated with the packet is located by the port and VLANs in the packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530774func (va *VoltApplication) ProcessUsDhcpv4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530775 logger.Infow(ctx, "Processing Southbound US DHCPv4 packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +0530776 // We received the packet on an access port and the service for the packet can be
777 // gotten from the port and the packet
778 vpv, svc := va.GetVnetFromPkt(device, port, pkt)
779 if vpv == nil {
780 logger.Warn(ctx, "VNET couldn't be found from packet")
781 return
782 }
783
784 outport, _ := va.GetNniPort(device)
785 if outport == "" || outport == "0" {
786 logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport})
787 return
788 }
789
790 // Extract the layers in the packet to prepare the outgoing packet
791 // We use the layers to build the outgoing packet from scratch as
792 // the packet received can't be modified to add/remove layers
793 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
794 ip := pkt.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
795 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
796 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
797 msgType := DhcpMsgType(dhcp4)
Naveen Sampath04696f72022-06-13 15:19:14 +0530798
799 // Learn the 8021P values from the packet received
800 var priority uint8
801 var dropEligible bool
802 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
803 if dot1ql != nil {
804 dot1q := dot1ql.(*layers.Dot1Q)
805 priority = dot1q.Priority
806 dropEligible = dot1q.DropEligible
807 }
808 // If this is the first message in the DHCP sequence, the service
809 // is added to the DHCP relay application. The reply packets locate
810 // the associated service/session from the relay application.
811 if msgType == layers.DHCPMsgTypeDiscover || msgType == layers.DHCPMsgTypeRequest {
812 if err := dhcpNws.AddDhcpSession(pkt, vpv); err != nil {
813 logger.Errorw(ctx, "Adding dhcp session failed", log.Fields{"Error": err})
814 }
815 }
816
817 // Raise mac-learnt(DHCP Discover) indication when mac learning is enabled and learnt mac
818 // is not same as received mac address. If mac learning disabled, we have mac address in the
819 // service configuration. Hence mac learnt indication is not raised
820 // Reset learnt mac address in case of DHCP release and raise the indication
821 if vpv.DhcpRelay {
822 // If this is the first message in the DHCP sequence, the service
823 // is added to the DHCP relay application. The reply packets locate
824 // the associated service/session from the relay application.
825 // DS HSIA flows will be added after DHCP ACK .
826 if msgType == layers.DHCPMsgTypeDiscover || msgType == layers.DHCPMsgTypeRequest {
827 if !util.MacAddrsMatch(vpv.MacAddr, dhcp4.ClientHWAddr) {
828 // MAC is different and relearning is disabled.
829 if NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn {
830 // update learnt mac for debug purpose
831 vpv.LearntMacAddr = dhcp4.ClientHWAddr
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530832 vpv.WriteToDb(cntx)
Naveen Sampath04696f72022-06-13 15:19:14 +0530833 logger.Warnw(ctx, "Dropping the packet Mac relearn is disabled",
834 log.Fields{"vpv.MacAddr": vpv.MacAddr, "LearntMac": dhcp4.ClientHWAddr})
835 return
836 }
837 expectedPort := va.GetMacInPortMap(dhcp4.ClientHWAddr)
838 if expectedPort != "" && expectedPort != vpv.Port {
839 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()})
840 return
841 }
842 }
843 }
844 raiseDHCPv4Indication(msgType, vpv, dhcp4.ClientHWAddr, vpv.Ipv4Addr, priority, device, 0)
845
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +0530846 // Check IsOption82Enabled flag in configuration. if true(enabled), add option82 into dhcpv4 header.
Naveen Sampath04696f72022-06-13 15:19:14 +0530847 // Remote id can be custom or mac address.
848 // If remote id is custom, then add service will carry the remote id
849 // If remote id is mac address, and if mac is configured, then add service will carry the remote id
850 // 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 +0530851 if svc.IsOption82Enabled {
Naveen Sampath04696f72022-06-13 15:19:14 +0530852 var remoteID []byte
853 if svc.RemoteIDType == string(MACAddress) {
854 remoteID = []byte((dhcp4.ClientHWAddr).String())
855 } else if svc.RemoteID != nil {
856 remoteID = svc.RemoteID
857 }
858 AddDhcpv4Option82(svc, remoteID, dhcp4)
859 }
860 }
861
862 buff := gopacket.NewSerializeBuffer()
863 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
864 logger.Error(ctx, "Error in setting checksum")
865 return
866 }
867 opts := gopacket.SerializeOptions{
868 FixLengths: true,
869 ComputeChecksums: true,
870 }
871
872 cTagType := layers.EthernetTypeIPv4
873 outerVlan, innerVlan := vpv.GetNniVlans()
874 logger.Debugw(ctx, "Vnet Vlans", log.Fields{"Svlan": outerVlan, "Cvlan": innerVlan})
875 eth.EthernetType = vpv.SVlanTpid
876
877 var pktLayers []gopacket.SerializableLayer
878 pktLayers = append(pktLayers, eth)
879
880 var qVlans []of.VlanType
881 var qVlanLayers []gopacket.SerializableLayer
882
883 if vpv.AllowTransparent {
884 nxtLayer := layers.EthernetTypeDot1Q
885 if vlans := GetVlans(pkt); len(vlans) > 1 {
886 qVlans = vlans[1:]
887 logger.Debugw(ctx, "Q Vlans", log.Fields{"Vlan List": qVlans})
888 cTagType = layers.EthernetTypeDot1Q
889 }
890 for i, qVlan := range qVlans {
891 vlan := uint16(qVlan)
892 if i == (len(qVlans) - 1) {
893 nxtLayer = layers.EthernetTypeIPv4
894 }
895 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
896 qVlanLayers = append(qVlanLayers, qdot1q)
897 }
898 }
899 switch vpv.VlanControl {
900 case ONUCVlanOLTSVlan,
901 OLTCVlanOLTSVlan:
902 sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q}
903 pktLayers = append(pktLayers, sdot1q)
904 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType}
905 pktLayers = append(pktLayers, cdot1q)
906 case ONUCVlan,
907 OLTSVlan,
908 None:
909 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType}
910 pktLayers = append(pktLayers, cdot1q)
911 default:
912 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
913 }
914
915 pktLayers = append(pktLayers, qVlanLayers...)
916 pktLayers = append(pktLayers, ip)
917 pktLayers = append(pktLayers, udp)
918 pktLayers = append(pktLayers, dhcp4)
919 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
920 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
921 return
922 }
923
924 // Now the packet constructed is output towards the switch to be emitted on
925 // the NNI port
926 if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil {
Akash Sonib3abf522022-12-19 13:20:02 +0530927 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error": err})
Naveen Sampath04696f72022-06-13 15:19:14 +0530928 }
929 if vpv.DhcpRelay {
930 // Inform dhcp request information to dhcp server handler
931 dhcpRequestReceived(uint16(vpv.CVlan), uint16(vpv.SVlan), eth.SrcMAC.String())
932 }
933}
934
935// ProcessUDP4Packet : CallBack function registered with application to handle DHCP packetIn
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530936func ProcessUDP4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
937 GetApplication().ProcessUDP4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530938}
939
940// ProcessUDP4Packet : The packet is a UDP packet and currently only DHCP relay application is supported
941// We determine the packet direction and process it based on the direction
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530942func (va *VoltApplication) ProcessUDP4Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +0530943 // Currently DHCP is the only application supported by the application
vinokuma926cb3e2023-03-29 11:41:06 +0530944 // We check for DHCP before proceeding further. In future, this could be
Naveen Sampath04696f72022-06-13 15:19:14 +0530945 // based on registration and the callbacks
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530946 logger.Debugw(ctx, "Process UDP4 Packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +0530947 dhcpl := pkt.Layer(layers.LayerTypeDHCPv4)
948 if dhcpl == nil {
949 return
950 }
951 //logger.Debugw(ctx, "Received Packet In", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
952 dhcp4 := pkt.Layer(layers.LayerTypeDHCPv4).(*layers.DHCPv4)
953 if dhcp4.Operation == layers.DHCPOpRequest {
954 // This is treated as an upstream packet in the VOLT application
955 // as VOLT serves access subscribers who use DHCP to acquire IP
956 // address and these packets go upstream to the network
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530957 va.ProcessUsDhcpv4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530958 } else {
959 // This is a downstream packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530960 va.ProcessDsDhcpv4Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530961 }
Naveen Sampath04696f72022-06-13 15:19:14 +0530962}
963
964// ProcessUDP6Packet : CallBack function registered with application to handle DHCPv6 packetIn
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530965func ProcessUDP6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
966 GetApplication().ProcessUDP6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530967}
968
969// ProcessUDP6Packet : As a LDRA node, we expect to see only RelayReply from the DHCP server and we always
970// pack the received request and send it to the server as a RelayForward message
971// We expect to see Solicit, Request in the most normal cases. Before the lease expires
972// we should also see Renew. However, we should always pack the US message by adding
973// additional option that identifies to the server that the DHCP packet is forwarded
974// by an LDRA node.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530975func (va *VoltApplication) ProcessUDP6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) []byte {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +0530976 logger.Debugw(ctx, "Processing DHCPv6 packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +0530977 dhcpl := pkt.Layer(layers.LayerTypeDHCPv6)
978 if dhcpl == nil {
979 return nil
980 }
Naveen Sampath04696f72022-06-13 15:19:14 +0530981 dhcpv6 := dhcpl.(*layers.DHCPv6)
982 switch dhcpv6.MsgType {
983 case layers.DHCPv6MsgTypeSolicit, layers.DHCPv6MsgTypeRequest, layers.DHCPv6MsgTypeRenew,
984 layers.DHCPv6MsgTypeRelease, layers.DHCPv6MsgTypeRebind, layers.DHCPv6MsgTypeInformationRequest,
985 layers.DHCPv6MsgTypeDecline:
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530986 va.ProcessUsDhcpv6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530987 case layers.DHCPv6MsgTypeAdvertise, layers.DHCPv6MsgTypeConfirm, layers.DHCPv6MsgTypeReconfigure:
988 logger.Warnw(ctx, "SouthBound DHCPv6 DS Messages Expected For a Relay Agent", log.Fields{"Type": dhcpv6.MsgType})
989 case layers.DHCPv6MsgTypeRelayForward:
990 logger.Warn(ctx, "As the first DHCPv6 Relay Agent, Unexpected Relay Forward")
991 case layers.DHCPv6MsgTypeRelayReply:
992 // We received a response from the server
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530993 va.ProcessDsDhcpv6Packet(cntx, device, port, pkt)
Naveen Sampath04696f72022-06-13 15:19:14 +0530994 }
995 return nil
996}
997
998// GetRelayReplyBytes to get relay reply bytes
999func GetRelayReplyBytes(dhcp6 *layers.DHCPv6) []byte {
1000 for _, o := range dhcp6.Options {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301001 logger.Debugw(ctx, "Received Option", log.Fields{"Code": o.Code})
Naveen Sampath04696f72022-06-13 15:19:14 +05301002 if o.Code == layers.DHCPv6OptRelayMessage {
1003 return o.Data
1004 }
1005 }
1006 return nil
1007}
1008
1009// BuildRelayFwd to build forward relay
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301010func BuildRelayFwd(paddr net.IP, intfID []byte, remoteID []byte, payload []byte, isOption82Enabled bool, dhcpRelay bool) *layers.DHCPv6 {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301011 logger.Debugw(ctx, "Build Relay Fwd", log.Fields{"Paddr": paddr, "isOption82Enabled": isOption82Enabled, "dhcpRelay": dhcpRelay})
Naveen Sampath04696f72022-06-13 15:19:14 +05301012 dhcp6 := &layers.DHCPv6{MsgType: layers.DHCPv6MsgTypeRelayForward, LinkAddr: net.ParseIP("::"), PeerAddr: []byte(paddr)}
1013 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptRelayMessage, payload))
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301014 // Check IsOption82Enabled flag in configuration. if true(enabled), add remoteID and circuitID into dhcpv6 header.
Naveen Sampath04696f72022-06-13 15:19:14 +05301015 if dhcpRelay {
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301016 if isOption82Enabled {
Naveen Sampath04696f72022-06-13 15:19:14 +05301017 remote := &layers.DHCPv6RemoteId{RemoteId: remoteID}
1018 if len(remoteID) != 0 {
1019 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptRemoteID, remote.Encode()))
1020 }
1021 if len(intfID) != 0 {
1022 intf := &layers.DHCPv6IntfId{Data: intfID}
1023 dhcp6.Options = append(dhcp6.Options, layers.NewDHCPv6Option(layers.DHCPv6OptInterfaceID, intf.Encode()))
1024 }
1025 }
1026 }
1027 return dhcp6
1028}
1029
vinokuma926cb3e2023-03-29 11:41:06 +05301030// nolint: gocyclo
Naveen Sampath04696f72022-06-13 15:19:14 +05301031// ProcessUsDhcpv6Packet to rpocess upstream DHCPv6 packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301032func (va *VoltApplication) ProcessUsDhcpv6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +05301033 // We received the packet on an access port and the service for the packet can be
1034 // gotten from the port and the packet
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301035 logger.Infow(ctx, "Processing Southbound US DHCPv6 packet", log.Fields{"Device": device, "Port": port})
Naveen Sampath04696f72022-06-13 15:19:14 +05301036 logger.Debugw(ctx, "Packet IN", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
1037 vpv, svc := va.GetVnetFromPkt(device, port, pkt)
1038 if vpv == nil {
1039 logger.Warn(ctx, "VNET couldn't be found from packet")
1040 return
1041 }
1042
1043 outport, _ := va.GetNniPort(device)
1044 if outport == "" || outport == "0" {
1045 logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport})
1046 return
1047 }
1048
1049 // Extract the layers in the packet to prepare the outgoing packet
1050 // We use the layers to build the outgoing packet from scratch as
1051 // the packet received can't be modified to add/remove layers
1052 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
1053 ip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
1054 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
1055 idhcp6 := pkt.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
1056
1057 // Remote id can be custom or mac address.
1058 // If remote id is custom, then add service will carry the remote id
1059 // If remote id is mac address, and if mac is configured, then add service will carry the remote id
1060 // If remote id is mac address, in mac learning case, then mac has to be taken from dhcp packet
1061 var remoteID []byte
1062 if svc.RemoteIDType == string(MACAddress) {
1063 remoteID = []byte((eth.SrcMAC).String())
1064 } else if svc.RemoteID != nil {
1065 remoteID = svc.RemoteID
1066 }
Hitesh Chhabra9f9a9df2023-06-13 17:52:15 +05301067 dhcp6 := BuildRelayFwd(ip.SrcIP, svc.GetCircuitID(), remoteID, udp.Payload, svc.IsOption82Enabled, vpv.DhcpRelay)
Naveen Sampath04696f72022-06-13 15:19:14 +05301068
1069 var sourceMac = eth.SrcMAC
1070 var sessionKey [MaxLenDhcpv6DUID]byte
1071
1072 clientDuid, decodedDuid := getDhcpv6ClientDUID(idhcp6)
1073 if clientDuid == nil || decodedDuid == nil {
1074 copy(sessionKey[:], eth.SrcMAC)
1075 } else {
1076 copy(sessionKey[:], clientDuid[0:])
1077 if decodedDuid.Type == layers.DHCPv6DUIDTypeLLT || decodedDuid.Type == layers.DHCPv6DUIDTypeLL {
1078 sourceMac = decodedDuid.LinkLayerAddress
1079 }
1080 }
1081 // Learn the 8021P values from the packet received
1082 var priority uint8
1083 var dropEligible bool
1084 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
1085 if dot1ql != nil {
1086 dot1q := dot1ql.(*layers.Dot1Q)
1087 priority = dot1q.Priority
1088 dropEligible = dot1q.DropEligible
1089 }
1090 if idhcp6.MsgType == layers.DHCPv6MsgTypeSolicit {
1091 if err := dhcpNws.AddDhcp6Session(sessionKey, vpv); err != nil {
1092 logger.Errorw(ctx, "Adding dhcpv6 session failed", log.Fields{"Error": err})
1093 }
1094 vpv.DHCPv6DUID = sessionKey
1095 }
1096
1097 // Raise mac-learnt(DHCPv6MsgTypeSolicit) indication when mac learning is enabled and learnt mac
1098 // is not same as received mac address. If mac learning disabled, we have mac address in the
1099 // service configuration. Hence mac learnt indication is not raised
1100 if vpv.DhcpRelay {
1101 if idhcp6.MsgType == layers.DHCPv6MsgTypeSolicit {
1102 if !util.MacAddrsMatch(vpv.MacAddr, sourceMac) {
1103 // MAC is different and relearning is disabled.
1104 if NonZeroMacAddress(vpv.MacAddr) && vpv.MacLearning == Learn {
1105 // update learnt mac for debug purpose
1106 vpv.LearntMacAddr = sourceMac
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301107 vpv.WriteToDb(cntx)
Naveen Sampath04696f72022-06-13 15:19:14 +05301108 logger.Warnw(ctx, "Dropping the packet Mac relearn is disabled",
1109 log.Fields{"vpv.MacAddr": vpv.MacAddr, "LearntMac": sourceMac})
1110 return
1111 }
1112 expectedPort := va.GetMacInPortMap(sourceMac)
1113 if expectedPort != "" && expectedPort != vpv.Port {
1114 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()})
1115 return
1116 }
1117 }
1118 }
1119 raiseDHCPv6Indication(idhcp6.MsgType, vpv, sourceMac, vpv.Ipv6Addr, priority, device, 0)
1120 }
1121
1122 // Create the buffer and the encode options for the outgoing packet
1123 buff := gopacket.NewSerializeBuffer()
1124 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
1125 logger.Error(ctx, "Error in setting checksum")
1126 return
1127 }
1128 opts := gopacket.SerializeOptions{
1129 FixLengths: true,
1130 ComputeChecksums: true,
1131 }
1132
1133 cTagType := layers.EthernetTypeIPv6
1134 outerVlan, innerVlan := vpv.GetNniVlans()
1135 eth.EthernetType = vpv.SVlanTpid
1136
1137 var pktLayers []gopacket.SerializableLayer
1138 pktLayers = append(pktLayers, eth)
1139
1140 var qVlans []of.VlanType
1141 var qVlanLayers []gopacket.SerializableLayer
1142
1143 if vpv.AllowTransparent {
1144 nxtLayer := layers.EthernetTypeDot1Q
1145 if vlans := GetVlans(pkt); len(vlans) > 1 {
1146 qVlans = vlans[1:]
1147 cTagType = layers.EthernetTypeDot1Q
1148 }
1149 for i, qVlan := range qVlans {
1150 vlan := uint16(qVlan)
1151 if i == (len(qVlans) - 1) {
1152 nxtLayer = layers.EthernetTypeIPv6
1153 }
1154 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
1155 qVlanLayers = append(qVlanLayers, qdot1q)
1156 }
Naveen Sampath04696f72022-06-13 15:19:14 +05301157 }
1158 switch vpv.VlanControl {
1159 case ONUCVlanOLTSVlan,
1160 OLTCVlanOLTSVlan:
1161 sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q}
1162 pktLayers = append(pktLayers, sdot1q)
1163 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType}
1164 pktLayers = append(pktLayers, cdot1q)
1165 case ONUCVlan,
1166 OLTSVlan,
1167 None:
1168 cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType}
1169 pktLayers = append(pktLayers, cdot1q)
1170 default:
1171 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
1172 }
1173
1174 pktLayers = append(pktLayers, qVlanLayers...)
1175 pktLayers = append(pktLayers, ip)
1176 pktLayers = append(pktLayers, udp)
1177 pktLayers = append(pktLayers, dhcp6)
1178 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
1179 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
1180 return
1181 }
1182 // Now the packet constructed is output towards the switch to be emitted on
1183 // the NNI port
1184 if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil {
Akash Sonib3abf522022-12-19 13:20:02 +05301185 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Error": err})
Naveen Sampath04696f72022-06-13 15:19:14 +05301186 }
1187 if vpv.DhcpRelay {
1188 // Inform dhcp request information to dhcp server handler
1189 dhcpRequestReceived(uint16(vpv.CVlan), uint16(vpv.SVlan), eth.SrcMAC.String())
1190 }
1191}
1192
1193// GetDhcpv6 to get dhcpv6 info
1194func GetDhcpv6(payload []byte) (*layers.DHCPv6, error) {
1195 pkt := gopacket.NewPacket(payload, layers.LayerTypeDHCPv6, gopacket.Default)
1196 if dl := pkt.Layer(layers.LayerTypeDHCPv6); dl != nil {
1197 if dhcp6, ok := dl.(*layers.DHCPv6); ok {
1198 return dhcp6, nil
1199 }
1200 }
1201 return nil, errors.New("Failed to decode DHCPv6")
1202}
1203
1204// ProcessDsDhcpv6Packet to process downstream dhcpv6 packet
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301205func (va *VoltApplication) ProcessDsDhcpv6Packet(cntx context.Context, device string, port string, pkt gopacket.Packet) {
Naveen Sampath04696f72022-06-13 15:19:14 +05301206 logger.Infow(ctx, "Processing Southbound DS DHCPv6 packet", log.Fields{"Port": port})
1207 logger.Debugw(ctx, "Packet IN", log.Fields{"Pkt": hex.EncodeToString(pkt.Data())})
1208
1209 // Retrieve the layers to build the outgoing packet. It is not
1210 // possible to add/remove layers to the existing packet and thus
1211 // the lyayers are extracted to build the outgoing packet
1212 // The DHCP layer is handled differently. The Relay-Reply option
1213 // of DHCP is extracted and is made the UDP payload.
1214 eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
1215 ip := pkt.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
1216 udp := pkt.Layer(layers.LayerTypeUDP).(*layers.UDP)
1217 idhcp6 := pkt.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
1218 //var dhcp6 *layers.DHCPv6
1219 var payload []byte
1220 if payload = GetRelayReplyBytes(idhcp6); payload == nil {
1221 logger.Warn(ctx, "Didn't Receive RelayMessage IE")
1222 return
1223 }
1224
1225 dhcp6, err := GetDhcpv6(payload)
1226 if err != nil {
1227 logger.Warnw(ctx, "DHCPv6 Decode Failed", log.Fields{"Reason": err.Error()})
1228 return
1229 }
1230
1231 // Learn the 8021P values from the packet received
1232 var priority uint8
1233 var dsPbit uint8
1234 var dropEligible bool
1235 dot1ql := pkt.Layer(layers.LayerTypeDot1Q)
1236 if dot1ql != nil {
1237 dot1q := dot1ql.(*layers.Dot1Q)
1238 priority = dot1q.Priority
1239 dropEligible = dot1q.DropEligible
1240 }
1241
1242 pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt)
1243 vpvList, clientMac, err := GetVnetForV6Nni(dhcp6, pktInnerlan, pktOuterlan, priority, eth.DstMAC)
1244 if len(vpvList) == 0 {
1245 logger.Warnw(ctx, "VNET couldn't be found for NNI", log.Fields{"Reason": err})
1246 return
1247 }
1248
1249 ipv6Addr, leaseTime := GetIpv6Addr(dhcp6)
1250
1251 for _, vpv := range vpvList {
Naveen Sampath04696f72022-06-13 15:19:14 +05301252 dsPbit = vpv.GetRemarkedPriority(priority)
1253 // Raise DHCPv6 Reply indication
1254 if vpv.DhcpRelay {
1255 // Inform dhcp response information to dhcp server handler
1256 dhcpResponseReceived(uint16(vpv.CVlan), uint16(vpv.SVlan))
1257
1258 if dhcp6.MsgType == layers.DHCPv6MsgTypeReply && ipv6Addr != nil {
1259 // separate go rotuine is spawned to avoid drop of ACK packet
1260 // as HSIA flows will be deleted if new MAC is learnt.
1261 if len(vpvList) == 1 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301262 go vpv.SetMacAddr(cntx, clientMac)
Naveen Sampath04696f72022-06-13 15:19:14 +05301263 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301264 vpv.Dhcpv6ResultInd(cntx, ipv6Addr, leaseTime)
Naveen Sampath04696f72022-06-13 15:19:14 +05301265 }
1266 raiseDHCPv6Indication(dhcp6.MsgType, vpv, clientMac, ipv6Addr, dsPbit, device, leaseTime)
1267 }
1268
1269 //Replace dst Port value to 546
1270 udp.DstPort = 546
1271 logger.Infow(ctx, "Packet Out UDP Port..", log.Fields{"UDP": udp, "Port": udp.DstPort})
1272
1273 // Create the buffer and the encode options for the outgoing packet
1274 buff := gopacket.NewSerializeBuffer()
1275 if err := udp.SetNetworkLayerForChecksum(ip); err != nil {
1276 logger.Error(ctx, "Error in setting checksum")
1277 return
1278 }
1279 opts := gopacket.SerializeOptions{
1280 FixLengths: true,
1281 ComputeChecksums: true,
1282 }
1283
1284 cTagType := layers.EthernetTypeIPv6
1285 eth.EthernetType = layers.EthernetTypeDot1Q
1286
1287 var pktLayers []gopacket.SerializableLayer
1288 pktLayers = append(pktLayers, eth)
1289
1290 var qVlans []of.VlanType
1291 var qVlanLayers []gopacket.SerializableLayer
1292
1293 if vpv.AllowTransparent {
1294 vlanThreshold := 2
1295 // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan
1296 // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan
1297 // Based on that, the no. of vlans should be ignored to get only transparent vlans
1298 if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None {
1299 vlanThreshold = 1
1300 }
1301 nxtLayer := layers.EthernetTypeDot1Q
1302 if vlans := GetVlans(pkt); len(vlans) > vlanThreshold {
1303 qVlans = vlans[vlanThreshold:]
1304 cTagType = layers.EthernetTypeDot1Q
1305 }
1306 for i, qVlan := range qVlans {
1307 vlan := uint16(qVlan)
1308 if i == (len(qVlans) - 1) {
1309 nxtLayer = layers.EthernetTypeIPv6
1310 }
1311 qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer}
1312 qVlanLayers = append(qVlanLayers, qdot1q)
1313 }
Naveen Sampath04696f72022-06-13 15:19:14 +05301314 }
1315 switch vpv.VlanControl {
1316 case ONUCVlanOLTSVlan:
1317 cdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType}
1318 pktLayers = append(pktLayers, cdot1q)
1319 case ONUCVlan,
1320 None:
1321 sdot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType}
1322 pktLayers = append(pktLayers, sdot1q)
1323 case OLTCVlanOLTSVlan,
1324 OLTSVlan:
1325 udot1q := &layers.Dot1Q{Priority: dsPbit, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType}
1326 pktLayers = append(pktLayers, udot1q)
1327 default:
1328 logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl})
1329 }
1330
1331 pktLayers = append(pktLayers, qVlanLayers...)
1332 pktLayers = append(pktLayers, ip)
1333 pktLayers = append(pktLayers, udp)
1334 pktLayers = append(pktLayers, dhcp6)
1335 logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)})
1336 if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil {
1337 logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()})
1338 return
1339 }
1340
1341 if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil {
1342 logger.Errorw(ctx, "PacketOutReq Failed", log.Fields{"Reason": err.Error()})
1343 }
1344 }
1345}
1346
1347// The DHCP relay application is maintained within the structures below
1348var dhcpNws *DhcpNetworks
1349
1350func init() {
1351 dhcpNws = NewDhcpNetworks()
1352}
Tinoj Josephec742f62022-09-29 19:11:10 +05301353
1354type DhcpAllocation struct {
Akash Sonib3abf522022-12-19 13:20:02 +05301355 SubscriberID string `json:"subscriberId"`
1356 ConnectPoint string `json:"connectPoint"`
vinokuma926cb3e2023-03-29 11:41:06 +05301357 AllocationTimeStamp time.Time `json:"allocationTimestamp"`
Akash Sonib3abf522022-12-19 13:20:02 +05301358 MacAddress net.HardwareAddr `json:"macAddress"`
vinokuma926cb3e2023-03-29 11:41:06 +05301359 CircuitID []byte `json:"circuitId"`
1360 IPAllocated net.IP `json:"ipAllocated"`
Akash Sonib3abf522022-12-19 13:20:02 +05301361 State int `json:"state"`
1362 VlanID int `json:"vlanId"`
Tinoj Josephec742f62022-09-29 19:11:10 +05301363}
1364
1365// GetAllocations returns DhcpAllocation info for all devices or for a device ID
Akash Sonib3abf522022-12-19 13:20:02 +05301366func (va *VoltApplication) GetAllocations(cntx context.Context, deviceID string) ([]DhcpAllocation, error) {
Tinoj Josephec742f62022-09-29 19:11:10 +05301367 logger.Debugw(ctx, "GetAllocations", log.Fields{"DeviceID": deviceID})
Akash Sonib3abf522022-12-19 13:20:02 +05301368 allocations := []DhcpAllocation{}
Tinoj Josephec742f62022-09-29 19:11:10 +05301369 for _, drv := range dhcpNws.Networks {
1370 drv.sessionLock.RLock()
1371 for _, session := range drv.sessions {
1372 vpv, ok := session.(*VoltPortVnet)
1373 if ok {
1374 var subscriber string
1375 // return Name of first service
1376 vpv.services.Range(func(key, value interface{}) bool {
1377 svc := value.(*VoltService)
1378 subscriber = svc.Name
1379 return false
1380 })
1381 // If deviceID is not provided, return all allocations
1382 // If deviceID exists then filter on deviceID
1383 if len(deviceID) == 0 || deviceID == vpv.Device {
Akash Sonib3abf522022-12-19 13:20:02 +05301384 allocation := DhcpAllocation{
1385 SubscriberID: subscriber,
1386 ConnectPoint: vpv.Device,
1387 MacAddress: vpv.MacAddr,
1388 State: int(vpv.RelayState),
1389 VlanID: int(vpv.SVlan),
1390 CircuitID: vpv.CircuitID,
vinokuma926cb3e2023-03-29 11:41:06 +05301391 IPAllocated: vpv.Ipv4Addr,
Akash Sonib3abf522022-12-19 13:20:02 +05301392 AllocationTimeStamp: vpv.DhcpExpiryTime,
1393 }
Tinoj Josephec742f62022-09-29 19:11:10 +05301394 logger.Debugw(ctx, "DHCP Allocation found", log.Fields{"DhcpAlloc": allocation})
1395 allocations = append(allocations, allocation)
1396 }
1397 }
1398 }
1399 drv.sessionLock.RUnlock()
1400 }
1401 return allocations, nil
1402}
Akash Sonib3abf522022-12-19 13:20:02 +05301403
1404type MacLearnerInfo struct {
vinokuma926cb3e2023-03-29 11:41:06 +05301405 DeviceID string `json:"deviceId"`
Akash Sonib3abf522022-12-19 13:20:02 +05301406 PortNumber string `json:"portNumber"`
vinokuma926cb3e2023-03-29 11:41:06 +05301407 VlanID string `json:"vlanId"`
Akash Sonib3abf522022-12-19 13:20:02 +05301408 MacAddress string `json:"macAddress"`
1409}
1410
1411func (va *VoltApplication) GetAllMacLearnerInfo() ([]MacLearnerInfo, error) {
1412 logger.Info(ctx, "GetMacLearnerInfo")
1413 macLearner := []MacLearnerInfo{}
1414 for _, drv := range dhcpNws.Networks {
1415 logger.Debugw(ctx, "drv found", log.Fields{"drv": drv})
1416 drv.sessionLock.RLock()
1417 for _, session := range drv.sessions {
1418 vpv, ok := session.(*VoltPortVnet)
1419 if ok {
1420 macLearn := MacLearnerInfo{
vinokuma926cb3e2023-03-29 11:41:06 +05301421 DeviceID: vpv.Device,
Akash Sonib3abf522022-12-19 13:20:02 +05301422 PortNumber: vpv.Port,
vinokuma926cb3e2023-03-29 11:41:06 +05301423 VlanID: vpv.SVlan.String(),
Akash Sonib3abf522022-12-19 13:20:02 +05301424 MacAddress: vpv.MacAddr.String(),
1425 }
1426 logger.Debugw(ctx, "MacLerner found", log.Fields{"MacLearn": macLearn})
1427 macLearner = append(macLearner, macLearn)
1428 }
1429 }
1430 drv.sessionLock.RUnlock()
1431 }
1432 return macLearner, nil
1433}
1434
vinokuma926cb3e2023-03-29 11:41:06 +05301435func (va *VoltApplication) GetMacLearnerInfo(cntx context.Context, deviceID, portNumber, vlanID string) (MacLearnerInfo, error) {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301436 logger.Debugw(ctx, "GetMecLearnerInfo", log.Fields{"DeviceID": deviceID, "PortNumber": portNumber, "VlanID": vlanID})
Akash Sonib3abf522022-12-19 13:20:02 +05301437 macLearn := MacLearnerInfo{}
1438 for _, drv := range dhcpNws.Networks {
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301439 logger.Debugw(ctx, "drv found", log.Fields{"drv": drv})
Akash Sonib3abf522022-12-19 13:20:02 +05301440 drv.sessionLock.RLock()
1441 for _, session := range drv.sessions {
1442 vpv, ok := session.(*VoltPortVnet)
1443 if ok {
vinokuma926cb3e2023-03-29 11:41:06 +05301444 if deviceID == vpv.Device && portNumber == vpv.Port && vlanID == vpv.SVlan.String() {
Akash Sonib3abf522022-12-19 13:20:02 +05301445 macLearn = MacLearnerInfo{
vinokuma926cb3e2023-03-29 11:41:06 +05301446 DeviceID: vpv.Device,
Akash Sonib3abf522022-12-19 13:20:02 +05301447 PortNumber: vpv.Port,
vinokuma926cb3e2023-03-29 11:41:06 +05301448 VlanID: vpv.SVlan.String(),
Akash Sonib3abf522022-12-19 13:20:02 +05301449 MacAddress: vpv.MacAddr.String(),
1450 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301451 logger.Debugw(ctx, "MacLerner found", log.Fields{"MacLearn": macLearn})
vinokuma926cb3e2023-03-29 11:41:06 +05301452 } else if deviceID == vpv.Device && portNumber == vpv.Port && vlanID == "" {
Akash Sonib3abf522022-12-19 13:20:02 +05301453 macLearn = MacLearnerInfo{
vinokuma926cb3e2023-03-29 11:41:06 +05301454 DeviceID: vpv.Device,
Akash Sonib3abf522022-12-19 13:20:02 +05301455 PortNumber: vpv.Port,
vinokuma926cb3e2023-03-29 11:41:06 +05301456 VlanID: vpv.SVlan.String(),
Akash Sonib3abf522022-12-19 13:20:02 +05301457 MacAddress: vpv.MacAddr.String(),
1458 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301459 logger.Debugw(ctx, "MacLerner found", log.Fields{"MacLearn": macLearn})
Akash Sonib3abf522022-12-19 13:20:02 +05301460 }
1461 }
1462 }
1463 drv.sessionLock.RUnlock()
1464 }
1465 return macLearn, nil
1466}
1467
1468func (va *VoltApplication) GetIgnoredPorts() (map[string][]string, error) {
1469 logger.Info(ctx, "GetIgnoredPorts")
1470 IgnoredPorts := make(map[string][]string)
1471 portIgnored := func(key, value interface{}) bool {
1472 voltDevice := value.(*VoltDevice)
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301473 logger.Debugw(ctx, "Inside GetIgnoredPorts method", log.Fields{"deviceName": voltDevice.Name})
Akash Sonib3abf522022-12-19 13:20:02 +05301474 voltDevice.Ports.Range(func(key, value interface{}) bool {
1475 port := key.(string)
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301476 logger.Debugw(ctx, "Inside GetIgnoredPorts method", log.Fields{"port": port})
Akash Sonib3abf522022-12-19 13:20:02 +05301477 //Obtain all VPVs associated with the port
1478 vnets, ok := GetApplication().VnetsByPort.Load(port)
1479 if !ok {
1480 return true
1481 }
1482 for _, vpv := range vnets.([]*VoltPortVnet) {
Akash Sonib3abf522022-12-19 13:20:02 +05301483 if vpv.MacLearning == MacLearningNone {
1484 IgnoredPorts[vpv.Device] = append(IgnoredPorts[vpv.Device], vpv.Port)
1485 }
1486 }
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301487 logger.Warnw(ctx, "Ignored Port", log.Fields{"Ignored Port": IgnoredPorts})
Akash Sonib3abf522022-12-19 13:20:02 +05301488 return true
1489 })
1490 return true
1491 }
1492 va.DevicesDisc.Range(portIgnored)
Hitesh Chhabra2b2347d2023-07-31 17:36:48 +05301493 logger.Debug(ctx, "GetIgnoredPorts completed")
Akash Sonib3abf522022-12-19 13:20:02 +05301494 return IgnoredPorts, nil
1495}