blob: eb8ebb3a8be3a47e8cc783dd55525bb807027570 [file] [log] [blame]
Takahiro Suzukid7bf8202020-12-17 20:21:59 +09001/*
2 * Copyright 2020-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package core
17
18import (
19 "context"
20 "encoding/binary"
21 "encoding/hex"
22 "fmt"
23 "time"
24
25 "github.com/google/gopacket"
26 "github.com/google/gopacket/layers"
27 "github.com/google/gopacket/pcap"
28 oop "github.com/opencord/voltha-protos/v3/go/openolt"
29)
30
31const vlanEnable = true
32const vlanIdManagement = 4090
33
34var svlanTagAuth = []byte{0x88, 0xa8, 0x0f, 0xfb}
35
36// IfName is a Interface Name of this device
37var IfName string
38
39// SrcMac is a MAC address of this device
40var SrcMac string
41
42var l2oamHandle *L2oamHandle
43
44// GetL2oamHandle returns my handle
45func GetL2oamHandle() *L2oamHandle {
46 return l2oamHandle
47}
48
49// L2oamHandle contains handle information
50type L2oamHandle struct {
51 Handle *pcap.Handle
52 ReqCh chan []byte
53}
54
55// NewL2oamHandle L2oamHandle created
56func NewL2oamHandle(ctx context.Context, ifname string, srcMac string) {
57 logger.Debug(ctx, fmt.Sprintf("L2oamHandle start. ifname=%s, srcMac=%s", ifname, srcMac))
58 IfName = ifname
59 SrcMac = srcMac
60 if GetL2oamHandle() == nil {
61 handle, _ := pcap.OpenLive(IfName, int32(1500), false, time.Millisecond*1000)
62 l2oamHandle = &L2oamHandle{
63 Handle: handle,
64 ReqCh: make(chan []byte),
65 }
66 if handle == nil {
67 logger.Debug(ctx, "could not get pcap handle.")
68 } else {
69 go l2oamHandle.startThread(context.Background())
70 }
71 }
72}
73
74// because of each handle object that is provided for a device receive messages respectively,
75// call this method using goroutine
76func (h *L2oamHandle) startThread(ctx context.Context) {
77 logger.Debug(ctx, "L2oamHandle thread start. ")
78
79 var filter string = "ether proto 0xa8c8 or 0x888e or 0x8100 or 0x88a8"
80 err := h.Handle.SetBPFFilter(filter)
81 if err != nil {
82 logger.Error(ctx, "Error: Handle.SetBPFFilter")
83 }
84
85 packetSource := gopacket.NewPacketSource(h.Handle, h.Handle.LinkType())
86
87 for {
88 select {
89 // send a message directly using send method, not sending it via channels
90 case message := <-h.ReqCh:
91 logger.Debug(ctx, fmt.Sprintf("gopacket send. %x", message))
92 if err := h.Handle.WritePacketData(message); err != nil {
93 logger.Debug(ctx, "write-packet-error")
94 }
95 // Packets are received
96 case packet := <-packetSource.Packets():
97 etherLayer := packet.Layer(layers.LayerTypeEthernet)
98 logger.Debug(ctx, fmt.Sprintf("gopacket receive. %x", etherLayer))
99 if etherLayer != nil {
100 // type assertion
101 etherPacket, _ := etherLayer.(*layers.Ethernet)
102 packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
103 logger.Debug(ctx, fmt.Sprintf("gopacket ether receive. %x", packetBytes))
104 if vlanEnable {
105 h.dispatchVlan(ctx, etherPacket)
106 } else {
107 h.dispatch(ctx, etherPacket)
108 }
109 }
110 }
111 }
112}
113func (h *L2oamHandle) dispatch(ctx context.Context, etherPacket *layers.Ethernet) {
114 logger.Debug(ctx, fmt.Sprintf("dispatch(). %x", etherPacket))
115
116 etherType := uint16(etherPacket.EthernetType)
117 if etherType == uint16(layers.EthernetTypeEAPOL) {
118 logger.Debug(ctx, fmt.Sprintf("receive message = EAPOL, Contents=%x, Payload=%x", etherPacket.Contents, etherPacket.Payload))
119 device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
120 if device == nil {
121 // unregisterd device
122 logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
123 h.packetIn(etherPacket)
124 } else {
125 device.receiveEapol(etherPacket)
126 }
127
128 } else if etherType == 0xa8c8 {
129 bytes := etherPacket.Payload
130 opcode := bytes[0]
131 oampduCode := bytes[3]
132 logger.Debug(ctx, fmt.Sprintf("receive message = 1904.2 OAM opcode=%v oampduCode=%v, %x", opcode, oampduCode, bytes))
133
134 device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
135 if device == nil {
136 // unregisterd device
137 logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
138 return
139 }
140
141 // OAM: keep-alive message
142 if opcode == 0x03 && oampduCode == 0x00 {
143 device.recieveKeepAlive(etherPacket)
144
145 } else {
146 device.recieve(etherPacket)
147 }
148 } else if etherType == uint16(layers.EthernetTypeDot1Q) {
149 device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
150 if device == nil {
151 // unregisterd device
152 logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
153 h.packetIn(etherPacket)
154 } else {
155 device.receiveEapol(etherPacket)
156 }
157
158 } else {
159 logger.Error(ctx, fmt.Sprintf("unknown etherType = 0x%X", etherType))
160 }
161}
162func (h *L2oamHandle) dispatchVlan(ctx context.Context, etherPacket *layers.Ethernet) {
163 logger.Debug(ctx, fmt.Sprintf("dispatchVlan(). %x", etherPacket))
164
165 etherType := uint16(etherPacket.EthernetType)
166 if etherType == uint16(layers.EthernetTypeQinQ) {
167 // Remove the VLAN tag
168 vlanPacket := &layers.Dot1Q{}
169 if err := vlanPacket.DecodeFromBytes(etherPacket.Payload, nil); err != nil {
170 logger.Debug(ctx, fmt.Sprintf("error:%v", err))
171 }
172 etherBytes := etherPacket.Contents
173 binary.BigEndian.PutUint16(etherBytes[12:], uint16(vlanPacket.Type))
174 packetBytes := append(etherBytes, vlanPacket.Payload...)
175 etherPacket = &layers.Ethernet{}
176 if err := etherPacket.DecodeFromBytes(packetBytes, nil); err != nil {
177 logger.Debug(ctx, fmt.Sprintf("error:%v", err))
178 }
179 h.dispatch(ctx, etherPacket)
180 } else {
181 logger.Error(ctx, fmt.Sprintf("Discard unsupported etherType. 0x%X", etherType))
182 }
183}
184
185func (h *L2oamHandle) send(message []byte) {
186 if h.Handle != nil {
187 h.ReqCh <- message
188 }
189}
190func (h *L2oamHandle) packetIn(etherPacket *layers.Ethernet) {
191 packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
192 pktInd := &oop.PacketIndication{
193 IntfId: 0,
194 GemportId: 1,
195 PortNo: 16,
196 Pkt: packetBytes,
197 IntfType: "pon",
198 }
199 logger.Info(context.Background(), fmt.Sprintf("L2oamHandle.packetIn() pktInd=%x", pktInd))
200
201 if err := l2oamDeviceHandler.handlePacketIndication(context.Background(), pktInd); err != nil {
202 logger.Error(context.Background(), "L2oamHandle.packetIn() error. ")
203 }
204}