blob: defbe1928dc6538b24fe4c0f51edb708a084d03c [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"
Anand S Katti09541352020-01-29 15:54:01 +053018 "net"
19 "time"
20
Arjun E K57a7fcb2020-01-30 06:44:45 +000021 "github.com/google/gopacket"
22 "github.com/google/gopacket/layers"
23 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
24 omci "github.com/opencord/omci-sim"
25 "github.com/opencord/voltha-protos/v2/go/openolt"
26 log "github.com/sirupsen/logrus"
Arjun E K57a7fcb2020-01-30 06:44:45 +000027)
28
29func SendIGMPLeaveGroupV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
30 log.WithFields(log.Fields{
31 "OnuId": onuId,
32 "SerialNumber": serialNumber,
33 "PortNo": portNo,
34 }).Debugf("Entered SendIGMPLeaveGroupV2")
35 igmp := createIGMPV2LeaveRequestPacket()
36 pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
37
38 if err != nil {
39 log.WithFields(log.Fields{
40 "OnuId": onuId,
41 "IntfId": ponPortId,
42 "SerialNumber": serialNumber,
43 }).Errorf("Seriliazation of igmp packet failed : %s", err)
44 return err
45 }
46
47 gemid, err := omci.GetGemPortId(ponPortId, onuId)
48 if err != nil {
49 log.WithFields(log.Fields{
50 "OnuId": onuId,
51 "IntfId": ponPortId,
52 "SerialNumber": serialNumber,
53 }).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
54 return err
55 }
56
57 data := &openolt.Indication_PktInd{
58 PktInd: &openolt.PacketIndication{
59 IntfType: "pon",
60 IntfId: ponPortId,
61 GemportId: uint32(gemid),
62 Pkt: pkt,
63 PortNo: portNo,
64 },
65 }
66 //Sending IGMP packets
67 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080068 log.WithFields(log.Fields{
69 "OnuId": onuId,
70 "SerialNumber": serialNumber,
71 "PortNo": portNo,
72 "IntfId": ponPortId,
73 "err": err,
74 }).Error("Fail to send IGMP PktInd indication for ONU")
Arjun E K57a7fcb2020-01-30 06:44:45 +000075 return err
76 }
77 return nil
78}
79
80func SendIGMPMembershipReportV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
81 log.WithFields(log.Fields{
82 "OnuId": onuId,
83 "SerialNumber": serialNumber,
84 "PortNo": portNo,
85 }).Debugf("Entered SendIGMPMembershipReportV2")
86 igmp := createIGMPV2MembershipReportPacket()
87 pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
88
89 if err != nil {
90 log.WithFields(log.Fields{
91 "OnuId": onuId,
92 "IntfId": ponPortId,
93 "SerialNumber": serialNumber,
94 }).Errorf("Seriliazation of igmp packet failed : %s", err)
95 return err
96 }
97
98 gemid, err := omci.GetGemPortId(ponPortId, onuId)
99 if err != nil {
100 log.WithFields(log.Fields{
101 "OnuId": onuId,
102 "IntfId": ponPortId,
103 "SerialNumber": serialNumber,
104 }).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
105 return err
106 }
107
108 data := &openolt.Indication_PktInd{
109 PktInd: &openolt.PacketIndication{
110 IntfType: "pon",
111 IntfId: ponPortId,
112 GemportId: uint32(gemid),
113 Pkt: pkt,
114 PortNo: portNo,
115 },
116 }
117 //Sending IGMP packets
118 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800119 log.WithFields(log.Fields{
120 "OnuId": onuId,
121 "SerialNumber": serialNumber,
122 "PortNo": portNo,
123 "IntfId": ponPortId,
124 "err": err,
125 }).Errorf("Fail to send IGMP PktInd indication")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000126 return err
127 }
128 return nil
129}
130
Arjun E Kdd443f02020-02-07 15:24:01 +0000131func SendIGMPMembershipReportV3(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
Anand S Katti09541352020-01-29 15:54:01 +0530132 log.WithFields(log.Fields{
133 "OnuId": onuId,
134 "SerialNumber": serialNumber,
135 "PortNo": portNo,
136 }).Debugf("Entered SendIGMPMembershipReportV3")
137 igmp := createIGMPV3MembershipReportPacket()
138 pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
Arjun E Kdd443f02020-02-07 15:24:01 +0000139
Anand S Katti09541352020-01-29 15:54:01 +0530140 if err != nil {
141 log.WithFields(log.Fields{
142 "OnuId": onuId,
143 "IntfId": ponPortId,
144 "SerialNumber": serialNumber,
145 }).Errorf("Seriliazation of igmp packet failed : %s", err)
146 return err
147 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000148
Anand S Katti09541352020-01-29 15:54:01 +0530149 gemid, err := omci.GetGemPortId(ponPortId, onuId)
150 if err != nil {
151 log.WithFields(log.Fields{
152 "OnuId": onuId,
153 "IntfId": ponPortId,
154 "SerialNumber": serialNumber,
155 }).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
156 return err
157 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000158
Anand S Katti09541352020-01-29 15:54:01 +0530159 data := &openolt.Indication_PktInd{
160 PktInd: &openolt.PacketIndication{
161 IntfType: "pon",
162 IntfId: ponPortId,
163 GemportId: uint32(gemid),
164 Pkt: pkt,
165 PortNo: portNo,
166 },
167 }
168 //Sending IGMP packets
169 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo446fc9e2020-03-13 15:48:13 -0700170 log.WithFields(log.Fields{
171 "OnuId": onuId,
172 "IntfId": ponPortId,
173 "SerialNumber": serialNumber,
174 "err": err,
175 }).Errorf("Fail to send IGMP PktInd indication")
Anand S Katti09541352020-01-29 15:54:01 +0530176 return err
177 }
178 return nil
Arjun E Kdd443f02020-02-07 15:24:01 +0000179}
180
181func createIGMPV3MembershipReportPacket() IGMP {
182
Anand S Katti09541352020-01-29 15:54:01 +0530183 groupRecord1 := IGMPv3GroupRecord{
184 Type: IGMPv3GroupRecordType(IGMPIsIn),
185 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
186 NumberOfSources: 3,
187 MulticastAddress: net.IPv4(224, 0, 0, 22),
188 SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 24), net.IPv4(15, 14, 20, 26), net.IPv4(15, 14, 20, 25)},
189 AuxData: 0, // NOT USED
190 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000191
Anand S Katti09541352020-01-29 15:54:01 +0530192 groupRecord2 := IGMPv3GroupRecord{
193 Type: IGMPv3GroupRecordType(IGMPIsIn),
194 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
195 NumberOfSources: 2,
196 MulticastAddress: net.IPv4(224, 0, 0, 25),
197 SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 30), net.IPv4(15, 14, 20, 31)},
198 AuxData: 0, // NOT USED
199 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000200
Anand S Katti09541352020-01-29 15:54:01 +0530201 igmpDefault := IGMP{
202 Type: 0x22, //IGMPV3 Membership Report
203 MaxResponseTime: time.Duration(1),
204 Checksum: 0,
205 GroupAddress: net.IPv4(224, 0, 0, 22),
206 SupressRouterProcessing: false,
207 RobustnessValue: 0,
208 IntervalTime: time.Duration(1),
209 SourceAddresses: []net.IP{net.IPv4(224, 0, 0, 24)},
210 NumberOfGroupRecords: 2,
211 NumberOfSources: 1,
212 GroupRecords: []IGMPv3GroupRecord{groupRecord1, groupRecord2},
213 Version: 3,
214 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000215
Anand S Katti09541352020-01-29 15:54:01 +0530216 return igmpDefault
Arjun E Kdd443f02020-02-07 15:24:01 +0000217}
218
Arjun E K57a7fcb2020-01-30 06:44:45 +0000219//func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp *layers.IGMP) ([]byte, error) {
220func createIGMPV2MembershipReportPacket() IGMP {
221 return IGMP{
222 Type: 0x16, //IGMPV2 Membership Report
223 MaxResponseTime: time.Duration(1),
224 Checksum: 0,
225 GroupAddress: net.IPv4(224, 0, 0, 22),
226 Version: 2,
227 }
228}
229
230func createIGMPV2LeaveRequestPacket() IGMP {
231 return IGMP{
232 Type: 0x17, //IGMPV2 Leave Group
233 MaxResponseTime: time.Duration(1),
234 Checksum: 0,
235 GroupAddress: net.IPv4(224, 0, 0, 22),
236 Version: 2,
237 }
238}
239
240func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp IGMP) ([]byte, error) {
241 buffer := gopacket.NewSerializeBuffer()
242 options := gopacket.SerializeOptions{
243 ComputeChecksums: true,
244 FixLengths: true,
245 }
246
247 ethernetLayer := &layers.Ethernet{
248 SrcMAC: srcMac,
249 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
250 EthernetType: layers.EthernetTypeIPv4,
251 }
252
253 ipLayer := &layers.IPv4{
254 Version: 4,
255 TOS: 0x10,
256 Id: 0,
257 TTL: 128,
258 SrcIP: []byte{0, 0, 0, 0},
259 DstIP: []byte{224, 0, 0, 22},
260 Protocol: layers.IPProtocolIGMP,
261 Options: []layers.IPv4Option{{OptionType: 148, OptionLength: 4, OptionData: make([]byte, 0)}}, //Adding router alert option
262 }
263
264 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, igmp); err != nil {
265 return nil, err
266 }
267
268 return buffer.Bytes(), nil
269}
270
271//-----------------------------------------***********************---------------------------------
272// BaseLayer is a convenience struct which implements the LayerData and
273// LayerPayload functions of the Layer interface.
274type BaseLayer struct {
275 // Contents is the set of bytes that make up this layer. IE: for an
276 // Ethernet packet, this would be the set of bytes making up the
277 // Ethernet frame.
278 Contents []byte
279 // Payload is the set of bytes contained by (but not part of) this
280 // Layer. Again, to take Ethernet as an example, this would be the
281 // set of bytes encapsulated by the Ethernet protocol.
282 Payload []byte
283}
284
285type IGMPType uint8
286
287type IGMP struct {
288 BaseLayer
289 Type IGMPType
290 MaxResponseTime time.Duration
291 Checksum uint16
292 GroupAddress net.IP
293 SupressRouterProcessing bool
294 RobustnessValue uint8
295 IntervalTime time.Duration
296 SourceAddresses []net.IP
297 NumberOfGroupRecords uint16
298 NumberOfSources uint16
Anand S Katti09541352020-01-29 15:54:01 +0530299 GroupRecords []IGMPv3GroupRecord
Arjun E K57a7fcb2020-01-30 06:44:45 +0000300 Version uint8 // IGMP protocol version
301}
302
Arjun E Kdd443f02020-02-07 15:24:01 +0000303// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
304type IGMPv3GroupRecord struct {
Anand S Katti09541352020-01-29 15:54:01 +0530305 Type IGMPv3GroupRecordType
306 AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
307 NumberOfSources uint16
308 MulticastAddress net.IP
309 SourceAddresses []net.IP
310 AuxData uint32 // NOT USED
Arjun E Kdd443f02020-02-07 15:24:01 +0000311}
312
313type IGMPv3GroupRecordType uint8
314
315const (
Anand S Katti09541352020-01-29 15:54:01 +0530316 IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
317 IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
318 IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
319 IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
320 IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
321 IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
Arjun E Kdd443f02020-02-07 15:24:01 +0000322)
323
324func (i IGMPv3GroupRecordType) String() string {
Anand S Katti09541352020-01-29 15:54:01 +0530325 switch i {
326 case IGMPIsIn:
327 return "MODE_IS_INCLUDE"
328 case IGMPIsEx:
329 return "MODE_IS_EXCLUDE"
330 case IGMPToIn:
331 return "CHANGE_TO_INCLUDE_MODE"
332 case IGMPToEx:
333 return "CHANGE_TO_EXCLUDE_MODE"
334 case IGMPAllow:
335 return "ALLOW_NEW_SOURCES"
336 case IGMPBlock:
337 return "BLOCK_OLD_SOURCES"
338 default:
339 return ""
340 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000341}
342
Arjun E K57a7fcb2020-01-30 06:44:45 +0000343// SerializeTo writes the serialized form of this layer into the
344// SerializationBuffer, implementing gopacket.SerializableLayer.
345// See the docs for gopacket.SerializableLayer for more info.
Arjun E K57a7fcb2020-01-30 06:44:45 +0000346func (igmp IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
Anand S Katti09541352020-01-29 15:54:01 +0530347 log.Debugf("Serializing IGMP Packet")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000348 data, err := b.PrependBytes(8915)
Anand S Katti09541352020-01-29 15:54:01 +0530349 if err != nil {
350 return err
351 }
352 if igmp.Version == 2 {
353 data[0] = byte(igmp.Type)
354 data[1] = byte(igmp.MaxResponseTime)
355 data[2] = 0
356 data[3] = 0
357 copy(data[4:8], igmp.GroupAddress.To4())
358 if opts.ComputeChecksums {
359 igmp.Checksum = tcpipChecksum(data, 0)
360 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
361 }
362 } else if igmp.Version == 3 {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000363
Anand S Katti09541352020-01-29 15:54:01 +0530364 data[0] = byte(igmp.Type)
365 data[1] = 0
366 data[2] = 0
367 data[3] = 0
368 data[4] = 0
369 data[5] = 0
370 binary.BigEndian.PutUint16(data[6:8], igmp.NumberOfGroupRecords)
371 j := 8
372 for i := uint16(0); i < igmp.NumberOfGroupRecords; i++ {
373 data[j] = byte(igmp.GroupRecords[i].Type)
374 data[j+1] = byte(0)
375 binary.BigEndian.PutUint16(data[j+2:j+4], igmp.GroupRecords[i].NumberOfSources)
376 copy(data[j+4:j+8], igmp.GroupRecords[i].MulticastAddress.To4())
377 j = j + 8
378 for m := uint16(0); m < igmp.GroupRecords[i].NumberOfSources; m++ {
379 copy(data[j:(j+4)], igmp.GroupRecords[i].SourceAddresses[m].To4())
380 j = j + 4
381 }
382 }
383 if opts.ComputeChecksums {
384 igmp.Checksum = tcpipChecksum(data, 0)
385 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
386 }
387 }
388 return nil
Arjun E K57a7fcb2020-01-30 06:44:45 +0000389}
390
391// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
392// initial checksum data that's already been computed.
393func tcpipChecksum(data []byte, csum uint32) uint16 {
394 // to handle odd lengths, we loop to length - 1, incrementing by 2, then
395 // handle the last byte specifically by checking against the original
396 // length.
397 length := len(data) - 1
398 for i := 0; i < length; i += 2 {
399 // For our test packet, doing this manually is about 25% faster
400 // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
401 csum += uint32(data[i]) << 8
402 csum += uint32(data[i+1])
403 }
404 if len(data)%2 == 1 {
405 csum += uint32(data[length]) << 8
406 }
407 for csum > 0xffff {
408 csum = (csum >> 16) + (csum & 0xffff)
409 }
410 return ^uint16(csum)
411}
412
413func (IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }