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