blob: e5c1ad237158e252a811069b354f47c2f8a9d880 [file] [log] [blame]
Arjun E K57a7fcb2020-01-30 06:44:45 +00001/*
2 * Copyright 2018-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 * http://www.apache.org/licenses/LICENSE-2.0
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 */
13
14package igmp
15
16import (
17 "encoding/binary"
18 "github.com/google/gopacket"
19 "github.com/google/gopacket/layers"
20 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
21 omci "github.com/opencord/omci-sim"
22 "github.com/opencord/voltha-protos/v2/go/openolt"
23 log "github.com/sirupsen/logrus"
24 "net"
25 "time"
26)
27
28func SendIGMPLeaveGroupV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
29 log.WithFields(log.Fields{
30 "OnuId": onuId,
31 "SerialNumber": serialNumber,
32 "PortNo": portNo,
33 }).Debugf("Entered SendIGMPLeaveGroupV2")
34 igmp := createIGMPV2LeaveRequestPacket()
35 pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
36
37 if err != nil {
38 log.WithFields(log.Fields{
39 "OnuId": onuId,
40 "IntfId": ponPortId,
41 "SerialNumber": serialNumber,
42 }).Errorf("Seriliazation of igmp packet failed : %s", err)
43 return err
44 }
45
46 gemid, err := omci.GetGemPortId(ponPortId, onuId)
47 if err != nil {
48 log.WithFields(log.Fields{
49 "OnuId": onuId,
50 "IntfId": ponPortId,
51 "SerialNumber": serialNumber,
52 }).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
53 return err
54 }
55
56 data := &openolt.Indication_PktInd{
57 PktInd: &openolt.PacketIndication{
58 IntfType: "pon",
59 IntfId: ponPortId,
60 GemportId: uint32(gemid),
61 Pkt: pkt,
62 PortNo: portNo,
63 },
64 }
65 //Sending IGMP packets
66 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080067 log.WithFields(log.Fields{
68 "OnuId": onuId,
69 "SerialNumber": serialNumber,
70 "PortNo": portNo,
71 "IntfId": ponPortId,
72 "err": err,
73 }).Error("Fail to send IGMP PktInd indication for ONU")
Arjun E K57a7fcb2020-01-30 06:44:45 +000074 return err
75 }
76 return nil
77}
78
79func SendIGMPMembershipReportV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
80 log.WithFields(log.Fields{
81 "OnuId": onuId,
82 "SerialNumber": serialNumber,
83 "PortNo": portNo,
84 }).Debugf("Entered SendIGMPMembershipReportV2")
85 igmp := createIGMPV2MembershipReportPacket()
86 pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
87
88 if err != nil {
89 log.WithFields(log.Fields{
90 "OnuId": onuId,
91 "IntfId": ponPortId,
92 "SerialNumber": serialNumber,
93 }).Errorf("Seriliazation of igmp packet failed : %s", err)
94 return err
95 }
96
97 gemid, err := omci.GetGemPortId(ponPortId, onuId)
98 if err != nil {
99 log.WithFields(log.Fields{
100 "OnuId": onuId,
101 "IntfId": ponPortId,
102 "SerialNumber": serialNumber,
103 }).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
104 return err
105 }
106
107 data := &openolt.Indication_PktInd{
108 PktInd: &openolt.PacketIndication{
109 IntfType: "pon",
110 IntfId: ponPortId,
111 GemportId: uint32(gemid),
112 Pkt: pkt,
113 PortNo: portNo,
114 },
115 }
116 //Sending IGMP packets
117 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800118 log.WithFields(log.Fields{
119 "OnuId": onuId,
120 "SerialNumber": serialNumber,
121 "PortNo": portNo,
122 "IntfId": ponPortId,
123 "err": err,
124 }).Errorf("Fail to send IGMP PktInd indication")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000125 return err
126 }
127 return nil
128}
129
130//func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp *layers.IGMP) ([]byte, error) {
131func createIGMPV2MembershipReportPacket() IGMP {
132 return IGMP{
133 Type: 0x16, //IGMPV2 Membership Report
134 MaxResponseTime: time.Duration(1),
135 Checksum: 0,
136 GroupAddress: net.IPv4(224, 0, 0, 22),
137 Version: 2,
138 }
139}
140
141func createIGMPV2LeaveRequestPacket() IGMP {
142 return IGMP{
143 Type: 0x17, //IGMPV2 Leave Group
144 MaxResponseTime: time.Duration(1),
145 Checksum: 0,
146 GroupAddress: net.IPv4(224, 0, 0, 22),
147 Version: 2,
148 }
149}
150
151func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp IGMP) ([]byte, error) {
152 buffer := gopacket.NewSerializeBuffer()
153 options := gopacket.SerializeOptions{
154 ComputeChecksums: true,
155 FixLengths: true,
156 }
157
158 ethernetLayer := &layers.Ethernet{
159 SrcMAC: srcMac,
160 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
161 EthernetType: layers.EthernetTypeIPv4,
162 }
163
164 ipLayer := &layers.IPv4{
165 Version: 4,
166 TOS: 0x10,
167 Id: 0,
168 TTL: 128,
169 SrcIP: []byte{0, 0, 0, 0},
170 DstIP: []byte{224, 0, 0, 22},
171 Protocol: layers.IPProtocolIGMP,
172 Options: []layers.IPv4Option{{OptionType: 148, OptionLength: 4, OptionData: make([]byte, 0)}}, //Adding router alert option
173 }
174
175 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, igmp); err != nil {
176 return nil, err
177 }
178
179 return buffer.Bytes(), nil
180}
181
182//-----------------------------------------***********************---------------------------------
183// BaseLayer is a convenience struct which implements the LayerData and
184// LayerPayload functions of the Layer interface.
185type BaseLayer struct {
186 // Contents is the set of bytes that make up this layer. IE: for an
187 // Ethernet packet, this would be the set of bytes making up the
188 // Ethernet frame.
189 Contents []byte
190 // Payload is the set of bytes contained by (but not part of) this
191 // Layer. Again, to take Ethernet as an example, this would be the
192 // set of bytes encapsulated by the Ethernet protocol.
193 Payload []byte
194}
195
196type IGMPType uint8
197
198type IGMP struct {
199 BaseLayer
200 Type IGMPType
201 MaxResponseTime time.Duration
202 Checksum uint16
203 GroupAddress net.IP
204 SupressRouterProcessing bool
205 RobustnessValue uint8
206 IntervalTime time.Duration
207 SourceAddresses []net.IP
208 NumberOfGroupRecords uint16
209 NumberOfSources uint16
210 Version uint8 // IGMP protocol version
211}
212
213// SerializeTo writes the serialized form of this layer into the
214// SerializationBuffer, implementing gopacket.SerializableLayer.
215// See the docs for gopacket.SerializableLayer for more info.
216// SerializeTo writes the serialized form of this layer into the
217// SerializationBuffer, implementing gopacket.SerializableLayer.
218// See the docs for gopacket.SerializableLayer for more info.
219func (igmp IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
220 // func (igmp *IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
221 log.Debugf("Serializing IGMP Packet")
222 //TODO - add length check here
223 data, err := b.PrependBytes(8915)
224 if err != nil {
225 return err
226 }
227
228 data[0] = byte(igmp.Type)
229 data[1] = byte(igmp.MaxResponseTime)
230 data[2] = 0
231 data[3] = 0
232 copy(data[4:8], igmp.GroupAddress.To4())
233 if opts.ComputeChecksums {
234 igmp.Checksum = tcpipChecksum(data, 0)
235 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
236 }
237 return nil
238}
239
240// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
241// initial checksum data that's already been computed.
242func tcpipChecksum(data []byte, csum uint32) uint16 {
243 // to handle odd lengths, we loop to length - 1, incrementing by 2, then
244 // handle the last byte specifically by checking against the original
245 // length.
246 length := len(data) - 1
247 for i := 0; i < length; i += 2 {
248 // For our test packet, doing this manually is about 25% faster
249 // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
250 csum += uint32(data[i]) << 8
251 csum += uint32(data[i+1])
252 }
253 if len(data)%2 == 1 {
254 csum += uint32(data[length]) << 8
255 }
256 for csum > 0xffff {
257 csum = (csum >> 16) + (csum & 0xffff)
258 }
259 return ^uint16(csum)
260}
261
262func (IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }