blob: a57810ed5adb3600962c8d4cf170867217d0a175 [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
Arjun E Kdd443f02020-02-07 15:24:01 +0000130func SendIGMPMembershipReportV3(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
131 log.WithFields(log.Fields{
132 "OnuId": onuId,
133 "SerialNumber": serialNumber,
134 "PortNo": portNo,
135 }).Debugf("Entered SendIGMPMembershipReportV3")
136 igmp := createIGMPV3MembershipReportPacket()
137 pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
138
139 if err != nil {
140 log.WithFields(log.Fields{
141 "OnuId": onuId,
142 "IntfId": ponPortId,
143 "SerialNumber": serialNumber,
144 }).Errorf("Seriliazation of igmp packet failed : %s", err)
145 return err
146 }
147
148 gemid, err := omci.GetGemPortId(ponPortId, onuId)
149 if err != nil {
150 log.WithFields(log.Fields{
151 "OnuId": onuId,
152 "IntfId": ponPortId,
153 "SerialNumber": serialNumber,
154 }).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
155 return err
156 }
157
158 data := &openolt.Indication_PktInd{
159 PktInd: &openolt.PacketIndication{
160 IntfType: "pon",
161 IntfId: ponPortId,
162 GemportId: uint32(gemid),
163 Pkt: pkt,
164 PortNo: portNo,
165 },
166 }
167 //Sending IGMP packets
168 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
169 log.Errorf("Fail to send IGMP PktInd indication for ONU: %s, IntfId: %s, SerialNumber: %s, error: %v", onuId, ponPortId, serialNumber, err)
170 return err
171 }
172 return nil
173}
174
175func createIGMPV3MembershipReportPacket() IGMP {
176
177 groupRecord1:= IGMPv3GroupRecord{
178 Type: IGMPv3GroupRecordType(IGMPIsIn),
179 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
180 NumberOfSources: 3,
181 MulticastAddress: net.IPv4(224, 0, 0, 22),
182 SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 24), net.IPv4(15, 14, 20, 26), net.IPv4(15, 14, 20, 25)},
183 AuxData: 0, // NOT USED
184 }
185
186 groupRecord2:= IGMPv3GroupRecord{
187 Type: IGMPv3GroupRecordType(IGMPIsIn),
188 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
189 NumberOfSources: 2,
190 MulticastAddress: net.IPv4(224, 0, 0, 25),
191 SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 30), net.IPv4(15, 14, 20, 31)},
192 AuxData: 0, // NOT USED
193 }
194
195 igmpDefault := IGMP{
196 Type: 0x22, //IGMPV3 Membership Report
197 MaxResponseTime: time.Duration(1),
198 Checksum: 0,
199 GroupAddress: net.IPv4(224, 0, 0, 22),
200 SupressRouterProcessing: false,
201 RobustnessValue: 0,
202 IntervalTime: time.Duration(1),
203 SourceAddresses: []net.IP{net.IPv4(224, 0, 0, 24)},
204 NumberOfGroupRecords: 2,
205 NumberOfSources: 1,
206 GroupRecords: []IGMPv3GroupRecord{groupRecord1, groupRecord2},
207 Version: 3,
208 }
209
210 return igmpDefault
211}
212
Arjun E K57a7fcb2020-01-30 06:44:45 +0000213//func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp *layers.IGMP) ([]byte, error) {
214func createIGMPV2MembershipReportPacket() IGMP {
215 return IGMP{
216 Type: 0x16, //IGMPV2 Membership Report
217 MaxResponseTime: time.Duration(1),
218 Checksum: 0,
219 GroupAddress: net.IPv4(224, 0, 0, 22),
220 Version: 2,
221 }
222}
223
224func createIGMPV2LeaveRequestPacket() IGMP {
225 return IGMP{
226 Type: 0x17, //IGMPV2 Leave Group
227 MaxResponseTime: time.Duration(1),
228 Checksum: 0,
229 GroupAddress: net.IPv4(224, 0, 0, 22),
230 Version: 2,
231 }
232}
233
234func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp IGMP) ([]byte, error) {
235 buffer := gopacket.NewSerializeBuffer()
236 options := gopacket.SerializeOptions{
237 ComputeChecksums: true,
238 FixLengths: true,
239 }
240
241 ethernetLayer := &layers.Ethernet{
242 SrcMAC: srcMac,
243 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
244 EthernetType: layers.EthernetTypeIPv4,
245 }
246
247 ipLayer := &layers.IPv4{
248 Version: 4,
249 TOS: 0x10,
250 Id: 0,
251 TTL: 128,
252 SrcIP: []byte{0, 0, 0, 0},
253 DstIP: []byte{224, 0, 0, 22},
254 Protocol: layers.IPProtocolIGMP,
255 Options: []layers.IPv4Option{{OptionType: 148, OptionLength: 4, OptionData: make([]byte, 0)}}, //Adding router alert option
256 }
257
258 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, igmp); err != nil {
259 return nil, err
260 }
261
262 return buffer.Bytes(), nil
263}
264
265//-----------------------------------------***********************---------------------------------
266// BaseLayer is a convenience struct which implements the LayerData and
267// LayerPayload functions of the Layer interface.
268type BaseLayer struct {
269 // Contents is the set of bytes that make up this layer. IE: for an
270 // Ethernet packet, this would be the set of bytes making up the
271 // Ethernet frame.
272 Contents []byte
273 // Payload is the set of bytes contained by (but not part of) this
274 // Layer. Again, to take Ethernet as an example, this would be the
275 // set of bytes encapsulated by the Ethernet protocol.
276 Payload []byte
277}
278
279type IGMPType uint8
280
281type IGMP struct {
282 BaseLayer
283 Type IGMPType
284 MaxResponseTime time.Duration
285 Checksum uint16
286 GroupAddress net.IP
287 SupressRouterProcessing bool
288 RobustnessValue uint8
289 IntervalTime time.Duration
290 SourceAddresses []net.IP
291 NumberOfGroupRecords uint16
292 NumberOfSources uint16
Arjun E Kdd443f02020-02-07 15:24:01 +0000293 GroupRecords []IGMPv3GroupRecord
Arjun E K57a7fcb2020-01-30 06:44:45 +0000294 Version uint8 // IGMP protocol version
295}
296
Arjun E Kdd443f02020-02-07 15:24:01 +0000297// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
298type IGMPv3GroupRecord struct {
299 Type IGMPv3GroupRecordType
300 AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
301 NumberOfSources uint16
302 MulticastAddress net.IP
303 SourceAddresses []net.IP
304 AuxData uint32 // NOT USED
305}
306
307type IGMPv3GroupRecordType uint8
308
309const (
310 IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
311 IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
312 IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
313 IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
314 IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
315 IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
316)
317
318func (i IGMPv3GroupRecordType) String() string {
319 switch i {
320 case IGMPIsIn:
321 return "MODE_IS_INCLUDE"
322 case IGMPIsEx:
323 return "MODE_IS_EXCLUDE"
324 case IGMPToIn:
325 return "CHANGE_TO_INCLUDE_MODE"
326 case IGMPToEx:
327 return "CHANGE_TO_EXCLUDE_MODE"
328 case IGMPAllow:
329 return "ALLOW_NEW_SOURCES"
330 case IGMPBlock:
331 return "BLOCK_OLD_SOURCES"
332 default:
333 return ""
334 }
335}
336
Arjun E K57a7fcb2020-01-30 06:44:45 +0000337// SerializeTo writes the serialized form of this layer into the
338// SerializationBuffer, implementing gopacket.SerializableLayer.
339// See the docs for gopacket.SerializableLayer for more info.
Arjun E K57a7fcb2020-01-30 06:44:45 +0000340func (igmp IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
Arjun E Kdd443f02020-02-07 15:24:01 +0000341 log.Debugf("Serializing IGMP Packet")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000342 data, err := b.PrependBytes(8915)
Arjun E Kdd443f02020-02-07 15:24:01 +0000343 if err != nil {
344 return err
345 }
346 if igmp.Version == 2 {
347 data[0] = byte(igmp.Type)
348 data[1] = byte(igmp.MaxResponseTime)
349 data[2] = 0
350 data[3] = 0
351 copy(data[4:8], igmp.GroupAddress.To4())
352 if opts.ComputeChecksums {
353 igmp.Checksum = tcpipChecksum(data, 0)
354 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
355 }
356 } else if igmp.Version ==3{
Arjun E K57a7fcb2020-01-30 06:44:45 +0000357
Arjun E Kdd443f02020-02-07 15:24:01 +0000358 data[0] = byte(igmp.Type)
359 data[1] = 0
360 data[2] = 0
361 data[3] = 0
362 data[4] = 0
363 data[5] = 0
364 binary.BigEndian.PutUint16(data[6:8], igmp.NumberOfGroupRecords)
365 j := 8
366 for i := uint16(0); i < igmp.NumberOfGroupRecords; i++ {
367 data[j] = byte(igmp.GroupRecords[i].Type)
368 data[j+1] = byte(0)
369 binary.BigEndian.PutUint16(data[j+2:j+4], igmp.GroupRecords[i].NumberOfSources)
370 copy(data[j+4:j+8], igmp.GroupRecords[i].MulticastAddress.To4())
371 j=j+8
372 for m := uint16(0); m < igmp.GroupRecords[i].NumberOfSources; m++ {
373 copy(data[j:(j+4)], igmp.GroupRecords[i].SourceAddresses[m].To4())
374 j=j+4
375 }
376 }
377 if opts.ComputeChecksums {
378 igmp.Checksum = tcpipChecksum(data, 0)
379 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
380 }
381 }
382 return nil
Arjun E K57a7fcb2020-01-30 06:44:45 +0000383}
384
385// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
386// initial checksum data that's already been computed.
387func tcpipChecksum(data []byte, csum uint32) uint16 {
388 // to handle odd lengths, we loop to length - 1, incrementing by 2, then
389 // handle the last byte specifically by checking against the original
390 // length.
391 length := len(data) - 1
392 for i := 0; i < length; i += 2 {
393 // For our test packet, doing this manually is about 25% faster
394 // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
395 csum += uint32(data[i]) << 8
396 csum += uint32(data[i+1])
397 }
398 if len(data)%2 == 1 {
399 csum += uint32(data[length]) << 8
400 }
401 for csum > 0xffff {
402 csum = (csum >> 16) + (csum & 0xffff)
403 }
404 return ^uint16(csum)
405}
406
407func (IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }