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