blob: ae1f36c286cf54a35cfb65a0973a58d5f03e7456 [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"
Matteo Scandolo618a6582020-09-09 12:21:29 -070018 "encoding/hex"
19 "errors"
20 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
Anand S Katti09541352020-01-29 15:54:01 +053021 "net"
22 "time"
23
Arjun E K57a7fcb2020-01-30 06:44:45 +000024 "github.com/google/gopacket"
25 "github.com/google/gopacket/layers"
26 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
Matteo Scandolo4f4ac792020-10-01 16:33:21 -070027 "github.com/opencord/voltha-protos/v4/go/openolt"
Arjun E K57a7fcb2020-01-30 06:44:45 +000028 log "github.com/sirupsen/logrus"
Arjun E K57a7fcb2020-01-30 06:44:45 +000029)
30
Matteo Scandolo618a6582020-09-09 12:21:29 -070031func SendIGMPLeaveGroupV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
32 gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream) error {
Arjun E K57a7fcb2020-01-30 06:44:45 +000033 log.WithFields(log.Fields{
34 "OnuId": onuId,
35 "SerialNumber": serialNumber,
36 "PortNo": portNo,
37 }).Debugf("Entered SendIGMPLeaveGroupV2")
38 igmp := createIGMPV2LeaveRequestPacket()
Matteo Scandolo618a6582020-09-09 12:21:29 -070039 pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
Arjun E K57a7fcb2020-01-30 06:44:45 +000040
41 if err != nil {
42 log.WithFields(log.Fields{
43 "OnuId": onuId,
44 "IntfId": ponPortId,
45 "SerialNumber": serialNumber,
46 }).Errorf("Seriliazation of igmp packet failed : %s", err)
47 return err
48 }
49
Arjun E K57a7fcb2020-01-30 06:44:45 +000050 data := &openolt.Indication_PktInd{
51 PktInd: &openolt.PacketIndication{
52 IntfType: "pon",
53 IntfId: ponPortId,
Matteo Scandolo618a6582020-09-09 12:21:29 -070054 GemportId: gemPortId,
Arjun E K57a7fcb2020-01-30 06:44:45 +000055 Pkt: pkt,
56 PortNo: portNo,
57 },
58 }
59 //Sending IGMP packets
60 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080061 log.WithFields(log.Fields{
62 "OnuId": onuId,
63 "SerialNumber": serialNumber,
64 "PortNo": portNo,
65 "IntfId": ponPortId,
66 "err": err,
67 }).Error("Fail to send IGMP PktInd indication for ONU")
Arjun E K57a7fcb2020-01-30 06:44:45 +000068 return err
69 }
70 return nil
71}
72
Matteo Scandolo618a6582020-09-09 12:21:29 -070073func SendIGMPMembershipReportV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
74 gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream) error {
75
Arjun E K57a7fcb2020-01-30 06:44:45 +000076 igmp := createIGMPV2MembershipReportPacket()
Matteo Scandolo618a6582020-09-09 12:21:29 -070077 pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
Arjun E K57a7fcb2020-01-30 06:44:45 +000078
79 if err != nil {
80 log.WithFields(log.Fields{
81 "OnuId": onuId,
82 "IntfId": ponPortId,
83 "SerialNumber": serialNumber,
84 }).Errorf("Seriliazation of igmp packet failed : %s", err)
85 return err
86 }
87
Arjun E K57a7fcb2020-01-30 06:44:45 +000088 data := &openolt.Indication_PktInd{
89 PktInd: &openolt.PacketIndication{
90 IntfType: "pon",
91 IntfId: ponPortId,
Matteo Scandolo618a6582020-09-09 12:21:29 -070092 GemportId: gemPortId,
Arjun E K57a7fcb2020-01-30 06:44:45 +000093 Pkt: pkt,
94 PortNo: portNo,
95 },
96 }
97 //Sending IGMP packets
98 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080099 log.WithFields(log.Fields{
100 "OnuId": onuId,
101 "SerialNumber": serialNumber,
102 "PortNo": portNo,
103 "IntfId": ponPortId,
104 "err": err,
105 }).Errorf("Fail to send IGMP PktInd indication")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000106 return err
107 }
Matteo Scandolo618a6582020-09-09 12:21:29 -0700108
109 log.WithFields(log.Fields{
110 "OnuId": onuId,
111 "SerialNumber": serialNumber,
112 "PortNo": portNo,
113 }).Debugf("Sent SendIGMPMembershipReportV2")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000114 return nil
115}
116
Matteo Scandolo618a6582020-09-09 12:21:29 -0700117func SendIGMPMembershipReportV3(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
118 gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream) error {
Anand S Katti09541352020-01-29 15:54:01 +0530119 log.WithFields(log.Fields{
120 "OnuId": onuId,
121 "SerialNumber": serialNumber,
122 "PortNo": portNo,
123 }).Debugf("Entered SendIGMPMembershipReportV3")
124 igmp := createIGMPV3MembershipReportPacket()
Matteo Scandolo618a6582020-09-09 12:21:29 -0700125 pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
Arjun E Kdd443f02020-02-07 15:24:01 +0000126
Anand S Katti09541352020-01-29 15:54:01 +0530127 if err != nil {
128 log.WithFields(log.Fields{
129 "OnuId": onuId,
130 "IntfId": ponPortId,
131 "SerialNumber": serialNumber,
132 }).Errorf("Seriliazation of igmp packet failed : %s", err)
133 return err
134 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000135
Anand S Katti09541352020-01-29 15:54:01 +0530136 data := &openolt.Indication_PktInd{
137 PktInd: &openolt.PacketIndication{
138 IntfType: "pon",
139 IntfId: ponPortId,
Matteo Scandolo618a6582020-09-09 12:21:29 -0700140 GemportId: gemPortId,
Anand S Katti09541352020-01-29 15:54:01 +0530141 Pkt: pkt,
142 PortNo: portNo,
143 },
144 }
145 //Sending IGMP packets
146 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo446fc9e2020-03-13 15:48:13 -0700147 log.WithFields(log.Fields{
148 "OnuId": onuId,
149 "IntfId": ponPortId,
150 "SerialNumber": serialNumber,
151 "err": err,
152 }).Errorf("Fail to send IGMP PktInd indication")
Anand S Katti09541352020-01-29 15:54:01 +0530153 return err
154 }
155 return nil
Arjun E Kdd443f02020-02-07 15:24:01 +0000156}
157
Matteo Scandolo618a6582020-09-09 12:21:29 -0700158func HandleNextPacket(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
159 gemPortId uint32, macAddress net.HardwareAddr, pkt gopacket.Packet, cTag int, pbit uint8, stream bbsim.Stream) error {
160
161 igmpLayer := pkt.Layer(layers.LayerTypeIGMP)
162 if igmpLayer == nil {
163 log.WithFields(log.Fields{
164 "OnuId": onuId,
165 "SerialNumber": serialNumber,
166 "PortNo": portNo,
167 "Pkt": hex.EncodeToString(pkt.Data()),
168 }).Error("This is not an IGMP packet")
169 return errors.New("packet-is-not-igmp")
170 }
171
172 log.WithFields(log.Fields{
173 "Pkt": pkt.Data(),
174 }).Trace("IGMP packet")
175
176 igmp := igmpLayer.(*layers.IGMPv1or2)
177
178 if igmp.Type == layers.IGMPMembershipQuery {
179 _ = SendIGMPMembershipReportV2(ponPortId, onuId, serialNumber, portNo, gemPortId, macAddress, cTag, pbit, stream)
180 }
181
182 return nil
183}
184
185func createIGMPV3MembershipReportPacket() *IGMP {
Arjun E Kdd443f02020-02-07 15:24:01 +0000186
Anand S Katti09541352020-01-29 15:54:01 +0530187 groupRecord1 := IGMPv3GroupRecord{
188 Type: IGMPv3GroupRecordType(IGMPIsIn),
189 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
190 NumberOfSources: 3,
191 MulticastAddress: net.IPv4(224, 0, 0, 22),
192 SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 24), net.IPv4(15, 14, 20, 26), net.IPv4(15, 14, 20, 25)},
193 AuxData: 0, // NOT USED
194 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000195
Anand S Katti09541352020-01-29 15:54:01 +0530196 groupRecord2 := IGMPv3GroupRecord{
197 Type: IGMPv3GroupRecordType(IGMPIsIn),
198 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
199 NumberOfSources: 2,
200 MulticastAddress: net.IPv4(224, 0, 0, 25),
201 SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 30), net.IPv4(15, 14, 20, 31)},
202 AuxData: 0, // NOT USED
203 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000204
Matteo Scandolo618a6582020-09-09 12:21:29 -0700205 igmpDefault := &IGMP{
206 Type: layers.IGMPMembershipReportV3, //IGMPV3 Membership Report
Anand S Katti09541352020-01-29 15:54:01 +0530207 MaxResponseTime: time.Duration(1),
208 Checksum: 0,
209 GroupAddress: net.IPv4(224, 0, 0, 22),
210 SupressRouterProcessing: false,
211 RobustnessValue: 0,
212 IntervalTime: time.Duration(1),
213 SourceAddresses: []net.IP{net.IPv4(224, 0, 0, 24)},
214 NumberOfGroupRecords: 2,
215 NumberOfSources: 1,
216 GroupRecords: []IGMPv3GroupRecord{groupRecord1, groupRecord2},
217 Version: 3,
218 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000219
Anand S Katti09541352020-01-29 15:54:01 +0530220 return igmpDefault
Arjun E Kdd443f02020-02-07 15:24:01 +0000221}
222
Matteo Scandolo618a6582020-09-09 12:21:29 -0700223func createIGMPV2MembershipReportPacket() *IGMP {
224 return &IGMP{
225 Type: layers.IGMPMembershipReportV2, //IGMPV2 Membership Report
226 Checksum: 0,
227 GroupAddress: net.IPv4(224, 0, 0, 22),
228 Version: 2,
229 }
230}
231
232func createIGMPV2LeaveRequestPacket() *IGMP {
233 return &IGMP{
234 Type: layers.IGMPLeaveGroup, //IGMPV2 Leave Group
Arjun E K57a7fcb2020-01-30 06:44:45 +0000235 MaxResponseTime: time.Duration(1),
236 Checksum: 0,
237 GroupAddress: net.IPv4(224, 0, 0, 22),
238 Version: 2,
239 }
240}
241
Matteo Scandolo618a6582020-09-09 12:21:29 -0700242func serializeIgmpPacket(intfId uint32, onuId uint32, cTag int, srcMac net.HardwareAddr, pbit uint8, igmp *IGMP) ([]byte, error) {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000243 buffer := gopacket.NewSerializeBuffer()
244 options := gopacket.SerializeOptions{
245 ComputeChecksums: true,
246 FixLengths: true,
247 }
248
249 ethernetLayer := &layers.Ethernet{
250 SrcMAC: srcMac,
251 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
252 EthernetType: layers.EthernetTypeIPv4,
253 }
254
255 ipLayer := &layers.IPv4{
256 Version: 4,
257 TOS: 0x10,
258 Id: 0,
259 TTL: 128,
260 SrcIP: []byte{0, 0, 0, 0},
261 DstIP: []byte{224, 0, 0, 22},
262 Protocol: layers.IPProtocolIGMP,
263 Options: []layers.IPv4Option{{OptionType: 148, OptionLength: 4, OptionData: make([]byte, 0)}}, //Adding router alert option
264 }
265
266 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, igmp); err != nil {
267 return nil, err
268 }
269
Matteo Scandolo618a6582020-09-09 12:21:29 -0700270 untaggedPkt := gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeEthernet, gopacket.Default)
271 taggedPkt, err := packetHandlers.PushSingleTag(cTag, untaggedPkt, pbit)
272
273 if err != nil {
274 log.Error("TagPacket")
275 return nil, err
276 }
277
278 return taggedPkt.Data(), nil
Arjun E K57a7fcb2020-01-30 06:44:45 +0000279}
280
281//-----------------------------------------***********************---------------------------------
Arjun E K57a7fcb2020-01-30 06:44:45 +0000282
283type IGMP struct {
Matteo Scandolo618a6582020-09-09 12:21:29 -0700284 layers.BaseLayer
285 Type layers.IGMPType
Arjun E K57a7fcb2020-01-30 06:44:45 +0000286 MaxResponseTime time.Duration
287 Checksum uint16
288 GroupAddress net.IP
289 SupressRouterProcessing bool
290 RobustnessValue uint8
291 IntervalTime time.Duration
292 SourceAddresses []net.IP
293 NumberOfGroupRecords uint16
294 NumberOfSources uint16
Anand S Katti09541352020-01-29 15:54:01 +0530295 GroupRecords []IGMPv3GroupRecord
Arjun E K57a7fcb2020-01-30 06:44:45 +0000296 Version uint8 // IGMP protocol version
297}
298
Arjun E Kdd443f02020-02-07 15:24:01 +0000299// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
300type IGMPv3GroupRecord struct {
Anand S Katti09541352020-01-29 15:54:01 +0530301 Type IGMPv3GroupRecordType
302 AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
303 NumberOfSources uint16
304 MulticastAddress net.IP
305 SourceAddresses []net.IP
306 AuxData uint32 // NOT USED
Arjun E Kdd443f02020-02-07 15:24:01 +0000307}
308
309type IGMPv3GroupRecordType uint8
310
311const (
Anand S Katti09541352020-01-29 15:54:01 +0530312 IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
313 IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
314 IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
315 IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
316 IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
317 IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
Arjun E Kdd443f02020-02-07 15:24:01 +0000318)
319
320func (i IGMPv3GroupRecordType) String() string {
Anand S Katti09541352020-01-29 15:54:01 +0530321 switch i {
322 case IGMPIsIn:
323 return "MODE_IS_INCLUDE"
324 case IGMPIsEx:
325 return "MODE_IS_EXCLUDE"
326 case IGMPToIn:
327 return "CHANGE_TO_INCLUDE_MODE"
328 case IGMPToEx:
329 return "CHANGE_TO_EXCLUDE_MODE"
330 case IGMPAllow:
331 return "ALLOW_NEW_SOURCES"
332 case IGMPBlock:
333 return "BLOCK_OLD_SOURCES"
334 default:
335 return ""
336 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000337}
338
Arjun E K57a7fcb2020-01-30 06:44:45 +0000339// SerializeTo writes the serialized form of this layer into the
340// SerializationBuffer, implementing gopacket.SerializableLayer.
341// See the docs for gopacket.SerializableLayer for more info.
Matteo Scandolo618a6582020-09-09 12:21:29 -0700342func (igmp *IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000343 data, err := b.PrependBytes(8915)
Anand S Katti09541352020-01-29 15:54:01 +0530344 if err != nil {
345 return err
346 }
347 if igmp.Version == 2 {
348 data[0] = byte(igmp.Type)
349 data[1] = byte(igmp.MaxResponseTime)
350 data[2] = 0
351 data[3] = 0
352 copy(data[4:8], igmp.GroupAddress.To4())
353 if opts.ComputeChecksums {
354 igmp.Checksum = tcpipChecksum(data, 0)
355 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
356 }
357 } else if igmp.Version == 3 {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000358
Anand S Katti09541352020-01-29 15:54:01 +0530359 data[0] = byte(igmp.Type)
360 data[1] = 0
361 data[2] = 0
362 data[3] = 0
363 data[4] = 0
364 data[5] = 0
365 binary.BigEndian.PutUint16(data[6:8], igmp.NumberOfGroupRecords)
366 j := 8
367 for i := uint16(0); i < igmp.NumberOfGroupRecords; i++ {
368 data[j] = byte(igmp.GroupRecords[i].Type)
369 data[j+1] = byte(0)
370 binary.BigEndian.PutUint16(data[j+2:j+4], igmp.GroupRecords[i].NumberOfSources)
371 copy(data[j+4:j+8], igmp.GroupRecords[i].MulticastAddress.To4())
372 j = j + 8
373 for m := uint16(0); m < igmp.GroupRecords[i].NumberOfSources; m++ {
374 copy(data[j:(j+4)], igmp.GroupRecords[i].SourceAddresses[m].To4())
375 j = j + 4
376 }
377 }
378 if opts.ComputeChecksums {
379 igmp.Checksum = tcpipChecksum(data, 0)
380 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
381 }
382 }
383 return nil
Arjun E K57a7fcb2020-01-30 06:44:45 +0000384}
385
386// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
387// initial checksum data that's already been computed.
388func tcpipChecksum(data []byte, csum uint32) uint16 {
389 // to handle odd lengths, we loop to length - 1, incrementing by 2, then
390 // handle the last byte specifically by checking against the original
391 // length.
392 length := len(data) - 1
393 for i := 0; i < length; i += 2 {
394 // For our test packet, doing this manually is about 25% faster
395 // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
396 csum += uint32(data[i]) << 8
397 csum += uint32(data[i+1])
398 }
399 if len(data)%2 == 1 {
400 csum += uint32(data[length]) << 8
401 }
402 for csum > 0xffff {
403 csum = (csum >> 16) + (csum & 0xffff)
404 }
405 return ^uint16(csum)
406}
407
Matteo Scandolo618a6582020-09-09 12:21:29 -0700408func (i *IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }
409func (i *IGMP) LayerContents() []byte { return i.Contents }
410func (i *IGMP) LayerPayload() []byte { return i.Payload }