blob: a64689238650c099339da9b00f5a6db77a82dd67 [file] [log] [blame]
Arjun E K57a7fcb2020-01-30 06:44:45 +00001/*
Joey Armstrong2c039362024-02-04 18:51:52 -05002 * Copyright 2018-2024 Open Networking Foundation (ONF) and the ONF Contributors
Arjun E K57a7fcb2020-01-30 06:44:45 +00003 * 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"
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030020 "fmt"
Anand S Katti09541352020-01-29 15:54:01 +053021 "net"
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030022 "strconv"
Anand S Katti09541352020-01-29 15:54:01 +053023 "time"
24
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030025 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
26
Arjun E K57a7fcb2020-01-30 06:44:45 +000027 "github.com/google/gopacket"
28 "github.com/google/gopacket/layers"
29 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
David K. Bainbridgec415efe2021-08-19 13:05:21 +000030 "github.com/opencord/voltha-protos/v5/go/openolt"
Arjun E K57a7fcb2020-01-30 06:44:45 +000031 log "github.com/sirupsen/logrus"
Arjun E K57a7fcb2020-01-30 06:44:45 +000032)
33
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030034func SendIGMPLeaveGroupV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, uniId uint32,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000035 gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream, groupAddress string) error {
Arjun E K57a7fcb2020-01-30 06:44:45 +000036 log.WithFields(log.Fields{
37 "OnuId": onuId,
38 "SerialNumber": serialNumber,
39 "PortNo": portNo,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000040 "GroupAddress": groupAddress,
Arjun E K57a7fcb2020-01-30 06:44:45 +000041 }).Debugf("Entered SendIGMPLeaveGroupV2")
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000042 igmp := createIGMPV2LeaveRequestPacket(groupAddress)
Matteo Scandolo618a6582020-09-09 12:21:29 -070043 pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
Arjun E K57a7fcb2020-01-30 06:44:45 +000044
45 if err != nil {
46 log.WithFields(log.Fields{
47 "OnuId": onuId,
48 "IntfId": ponPortId,
49 "SerialNumber": serialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000050 "GroupAddress": groupAddress,
Arjun E K57a7fcb2020-01-30 06:44:45 +000051 }).Errorf("Seriliazation of igmp packet failed : %s", err)
52 return err
53 }
54
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030055 // For IGMP testing, if voltha is not connected we do not have
56 // gemPortId, so set a unique gemPortId per ONU
57 if gemPortId == 0 {
58 gid, _ := strconv.ParseUint(fmt.Sprintf("%d%d", ponPortId, onuId), 10, 32)
59 gemPortId = uint32(gid)
60 }
61
Arjun E K57a7fcb2020-01-30 06:44:45 +000062 data := &openolt.Indication_PktInd{
63 PktInd: &openolt.PacketIndication{
64 IntfType: "pon",
65 IntfId: ponPortId,
Matteo Scandolo618a6582020-09-09 12:21:29 -070066 GemportId: gemPortId,
Arjun E K57a7fcb2020-01-30 06:44:45 +000067 Pkt: pkt,
68 PortNo: portNo,
Girish Gowdra62f24292021-05-12 16:28:39 -070069 OnuId: onuId,
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030070 UniId: uniId,
Arjun E K57a7fcb2020-01-30 06:44:45 +000071 },
72 }
73 //Sending IGMP packets
74 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080075 log.WithFields(log.Fields{
76 "OnuId": onuId,
77 "SerialNumber": serialNumber,
78 "PortNo": portNo,
79 "IntfId": ponPortId,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000080 "GroupAddress": groupAddress,
Matteo Scandolo583f17d2020-02-13 10:35:17 -080081 "err": err,
82 }).Error("Fail to send IGMP PktInd indication for ONU")
Arjun E K57a7fcb2020-01-30 06:44:45 +000083 return err
84 }
85 return nil
86}
87
Baris Ertas0ce2f7c2023-04-11 11:44:24 +030088func SendIGMPMembershipReportV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, uniId uint32,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000089 gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream, groupAddress string) error {
Matteo Scandolo618a6582020-09-09 12:21:29 -070090
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000091 igmp := createIGMPV2MembershipReportPacket(groupAddress)
Matteo Scandolo618a6582020-09-09 12:21:29 -070092 pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
Arjun E K57a7fcb2020-01-30 06:44:45 +000093
94 if err != nil {
95 log.WithFields(log.Fields{
96 "OnuId": onuId,
97 "IntfId": ponPortId,
98 "SerialNumber": serialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +000099 "GroupAddress": groupAddress,
Arjun E K57a7fcb2020-01-30 06:44:45 +0000100 }).Errorf("Seriliazation of igmp packet failed : %s", err)
101 return err
102 }
103
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300104 // For IGMP testing, if voltha is not connected we do not have
105 // gemPortId, so set a unique gemPortId per ONU
106 if gemPortId == 0 {
107 gid, _ := strconv.ParseUint(fmt.Sprintf("%d%d", ponPortId, onuId), 10, 32)
108 gemPortId = uint32(gid)
109 }
110
Arjun E K57a7fcb2020-01-30 06:44:45 +0000111 data := &openolt.Indication_PktInd{
112 PktInd: &openolt.PacketIndication{
113 IntfType: "pon",
114 IntfId: ponPortId,
Matteo Scandolo618a6582020-09-09 12:21:29 -0700115 GemportId: gemPortId,
Arjun E K57a7fcb2020-01-30 06:44:45 +0000116 Pkt: pkt,
117 PortNo: portNo,
Girish Gowdra62f24292021-05-12 16:28:39 -0700118 OnuId: onuId,
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300119 UniId: uniId,
Arjun E K57a7fcb2020-01-30 06:44:45 +0000120 },
121 }
122 //Sending IGMP packets
123 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800124 log.WithFields(log.Fields{
125 "OnuId": onuId,
126 "SerialNumber": serialNumber,
127 "PortNo": portNo,
128 "IntfId": ponPortId,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000129 "GroupAddress": groupAddress,
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800130 "err": err,
131 }).Errorf("Fail to send IGMP PktInd indication")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000132 return err
133 }
Matteo Scandolo618a6582020-09-09 12:21:29 -0700134
135 log.WithFields(log.Fields{
136 "OnuId": onuId,
137 "SerialNumber": serialNumber,
138 "PortNo": portNo,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000139 "GroupAddress": groupAddress,
Matteo Scandolo618a6582020-09-09 12:21:29 -0700140 }).Debugf("Sent SendIGMPMembershipReportV2")
Arjun E K57a7fcb2020-01-30 06:44:45 +0000141 return nil
142}
143
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300144func SendIGMPMembershipReportV3(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, uniId uint32,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000145 gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream, groupAddress string) error {
146
Anand S Katti09541352020-01-29 15:54:01 +0530147 log.WithFields(log.Fields{
148 "OnuId": onuId,
149 "SerialNumber": serialNumber,
150 "PortNo": portNo,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000151 "GroupAddress": groupAddress,
Anand S Katti09541352020-01-29 15:54:01 +0530152 }).Debugf("Entered SendIGMPMembershipReportV3")
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000153 igmp := createIGMPV3MembershipReportPacket(groupAddress)
Matteo Scandolo618a6582020-09-09 12:21:29 -0700154 pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
Arjun E Kdd443f02020-02-07 15:24:01 +0000155
Anand S Katti09541352020-01-29 15:54:01 +0530156 if err != nil {
157 log.WithFields(log.Fields{
158 "OnuId": onuId,
159 "IntfId": ponPortId,
160 "SerialNumber": serialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000161 "GroupAddress": groupAddress,
Anand S Katti09541352020-01-29 15:54:01 +0530162 }).Errorf("Seriliazation of igmp packet failed : %s", err)
163 return err
164 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000165
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300166 // For IGMP testing, if voltha is not connected we do not have
167 // gemPortId, so set a unique gemPortId per ONU
168 if gemPortId == 0 {
169 gid, _ := strconv.ParseUint(fmt.Sprintf("%d%d", ponPortId, onuId), 10, 32)
170 gemPortId = uint32(gid)
171 }
172
Anand S Katti09541352020-01-29 15:54:01 +0530173 data := &openolt.Indication_PktInd{
174 PktInd: &openolt.PacketIndication{
175 IntfType: "pon",
176 IntfId: ponPortId,
Matteo Scandolo618a6582020-09-09 12:21:29 -0700177 GemportId: gemPortId,
Anand S Katti09541352020-01-29 15:54:01 +0530178 Pkt: pkt,
179 PortNo: portNo,
Girish Gowdra62f24292021-05-12 16:28:39 -0700180 OnuId: onuId,
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300181 UniId: uniId,
Anand S Katti09541352020-01-29 15:54:01 +0530182 },
183 }
184 //Sending IGMP packets
185 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
Matteo Scandolo446fc9e2020-03-13 15:48:13 -0700186 log.WithFields(log.Fields{
187 "OnuId": onuId,
188 "IntfId": ponPortId,
189 "SerialNumber": serialNumber,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000190 "GroupAddress": groupAddress,
Matteo Scandolo446fc9e2020-03-13 15:48:13 -0700191 "err": err,
192 }).Errorf("Fail to send IGMP PktInd indication")
Anand S Katti09541352020-01-29 15:54:01 +0530193 return err
194 }
195 return nil
Arjun E Kdd443f02020-02-07 15:24:01 +0000196}
197
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300198func HandleNextPacket(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, uniId uint32,
Baris Ertas53ab13c2023-05-25 16:31:48 +0300199 gemPortId uint32, macAddress net.HardwareAddr, pkt gopacket.Packet, cTag int, pbit uint8, groupAddresses map[string]int, stream bbsim.Stream) error {
200
201 dot1qLayer := pkt.Layer(layers.LayerTypeDot1Q)
202 if dot1qLayer == nil {
203 log.WithFields(log.Fields{"Pkt": hex.EncodeToString(pkt.Data())}).Warnf("packet-is-not-tagged")
204 return errors.New("packet-is-not-tagged")
205 }
206 dot1q := dot1qLayer.(*layers.Dot1Q)
Matteo Scandolo618a6582020-09-09 12:21:29 -0700207
208 igmpLayer := pkt.Layer(layers.LayerTypeIGMP)
209 if igmpLayer == nil {
210 log.WithFields(log.Fields{
211 "OnuId": onuId,
212 "SerialNumber": serialNumber,
213 "PortNo": portNo,
214 "Pkt": hex.EncodeToString(pkt.Data()),
215 }).Error("This is not an IGMP packet")
216 return errors.New("packet-is-not-igmp")
217 }
218
219 log.WithFields(log.Fields{
Baris Ertas53ab13c2023-05-25 16:31:48 +0300220 "Pkt": hex.EncodeToString(pkt.Data()),
Matteo Scandolo618a6582020-09-09 12:21:29 -0700221 }).Trace("IGMP packet")
222
Baris Ertas53ab13c2023-05-25 16:31:48 +0300223 var igmp *IGMP
224 igmpv1or2, ok := igmpLayer.(*layers.IGMPv1or2)
225 if ok {
226 igmp = &IGMP{
227 Type: igmpv1or2.Type,
228 MaxResponseTime: igmpv1or2.MaxResponseTime,
229 Checksum: igmpv1or2.Checksum,
230 GroupAddress: igmpv1or2.GroupAddress,
231 Version: igmpv1or2.Version,
232 }
233 } else {
234 igmpv3, ok := igmpLayer.(*layers.IGMP)
235 if ok {
236 igmp = &IGMP{
237 Type: igmpv3.Type,
238 MaxResponseTime: igmpv3.MaxResponseTime,
239 Checksum: igmpv3.Checksum,
240 GroupAddress: igmpv3.GroupAddress,
241 SupressRouterProcessing: igmpv3.SupressRouterProcessing,
242 RobustnessValue: igmpv3.RobustnessValue,
243 IntervalTime: igmpv3.IntervalTime,
244 SourceAddresses: igmpv3.SourceAddresses,
245 NumberOfGroupRecords: igmpv3.NumberOfGroupRecords,
246 NumberOfSources: igmpv3.NumberOfSources,
247 GroupRecords: igmpv3.GroupRecords,
248 Version: igmpv3.Version,
249 }
250 } else {
251 log.Warnf("could-not-parse-igmp-packet")
252 }
253 }
Matteo Scandolo618a6582020-09-09 12:21:29 -0700254
255 if igmp.Type == layers.IGMPMembershipQuery {
Baris Ertas53ab13c2023-05-25 16:31:48 +0300256 // Send response to queries only if joined to a group
257 for groupAddr, vlan := range groupAddresses {
258 if vlan == int(dot1q.VLANIdentifier) {
259 _ = SendIGMPMembershipReportV2(ponPortId, onuId, serialNumber, portNo, uniId, gemPortId, macAddress,
260 vlan, pbit, stream, groupAddr)
261 }
262 }
263 if groupAddresses == nil {
264 log.WithFields(log.Fields{
265 "OnuId": onuId,
266 "SerialNumber": serialNumber,
267 "PortNo": portNo,
268 "Pkt": hex.EncodeToString(pkt.Data()),
269 }).Trace("no-active-channels")
270 }
Matteo Scandolo618a6582020-09-09 12:21:29 -0700271 }
272
273 return nil
274}
275
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000276func createIGMPV3MembershipReportPacket(groupAddress string) *IGMP {
Arjun E Kdd443f02020-02-07 15:24:01 +0000277
Baris Ertas53ab13c2023-05-25 16:31:48 +0300278 // groupRecord1 := IGMPv3GroupRecord{
279 // Type: IGMPv3GroupRecordType(IGMPIsIn),
280 // AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
281 // NumberOfSources: 3,
282 // MulticastAddress: net.IPv4(224, 0, 0, 22),
283 // SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 24), net.IPv4(15, 14, 20, 26), net.IPv4(15, 14, 20, 25)},
284 // AuxData: 0, // NOT USED
285 // }
Arjun E Kdd443f02020-02-07 15:24:01 +0000286
Baris Ertas53ab13c2023-05-25 16:31:48 +0300287 // Keeping this as an example
288 // groupRecord1 := IGMPv3GroupRecord{
289 // Type: IGMPv3GroupRecordType(IGMPIsIn),
290 // AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
291 // NumberOfSources: 3,
292 // MulticastAddress: net.IPv4(224, 0, 0, 22),
293 // SourceAddresses: []net.IP{net.IPv4(15, 14, 20, 24), net.IPv4(15, 14, 20, 26), net.IPv4(15, 14, 20, 25)},
294 // AuxData: 0, // NOT USED
295 // }
296
297 groupRecord := layers.IGMPv3GroupRecord{
298 Type: layers.IGMPv3GroupRecordType(IGMPIsEx),
Anand S Katti09541352020-01-29 15:54:01 +0530299 AuxDataLen: 0, // this should always be 0 as per IGMPv3 spec.
Baris Ertas53ab13c2023-05-25 16:31:48 +0300300 NumberOfSources: 0,
301 MulticastAddress: net.ParseIP(groupAddress),
Anand S Katti09541352020-01-29 15:54:01 +0530302 AuxData: 0, // NOT USED
303 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000304
Matteo Scandolo618a6582020-09-09 12:21:29 -0700305 igmpDefault := &IGMP{
306 Type: layers.IGMPMembershipReportV3, //IGMPV3 Membership Report
Anand S Katti09541352020-01-29 15:54:01 +0530307 MaxResponseTime: time.Duration(1),
308 Checksum: 0,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000309 GroupAddress: net.ParseIP(groupAddress),
Anand S Katti09541352020-01-29 15:54:01 +0530310 SupressRouterProcessing: false,
311 RobustnessValue: 0,
312 IntervalTime: time.Duration(1),
Baris Ertas53ab13c2023-05-25 16:31:48 +0300313 NumberOfGroupRecords: 1,
314 NumberOfSources: 0,
315 GroupRecords: []layers.IGMPv3GroupRecord{groupRecord},
Anand S Katti09541352020-01-29 15:54:01 +0530316 Version: 3,
317 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000318
Anand S Katti09541352020-01-29 15:54:01 +0530319 return igmpDefault
Arjun E Kdd443f02020-02-07 15:24:01 +0000320}
321
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000322func createIGMPV2MembershipReportPacket(groupAddress string) *IGMP {
Matteo Scandolo618a6582020-09-09 12:21:29 -0700323 return &IGMP{
324 Type: layers.IGMPMembershipReportV2, //IGMPV2 Membership Report
325 Checksum: 0,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000326 GroupAddress: net.ParseIP(groupAddress),
Matteo Scandolo618a6582020-09-09 12:21:29 -0700327 Version: 2,
328 }
329}
330
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000331func createIGMPV2LeaveRequestPacket(groupAddress string) *IGMP {
Matteo Scandolo618a6582020-09-09 12:21:29 -0700332 return &IGMP{
333 Type: layers.IGMPLeaveGroup, //IGMPV2 Leave Group
Arjun E K57a7fcb2020-01-30 06:44:45 +0000334 MaxResponseTime: time.Duration(1),
335 Checksum: 0,
Onur Kalinagac9f9faca2021-01-21 14:04:34 +0000336 GroupAddress: net.ParseIP(groupAddress),
Arjun E K57a7fcb2020-01-30 06:44:45 +0000337 Version: 2,
338 }
339}
340
Matteo Scandolo618a6582020-09-09 12:21:29 -0700341func serializeIgmpPacket(intfId uint32, onuId uint32, cTag int, srcMac net.HardwareAddr, pbit uint8, igmp *IGMP) ([]byte, error) {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000342 buffer := gopacket.NewSerializeBuffer()
343 options := gopacket.SerializeOptions{
344 ComputeChecksums: true,
345 FixLengths: true,
346 }
347
348 ethernetLayer := &layers.Ethernet{
349 SrcMAC: srcMac,
350 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
351 EthernetType: layers.EthernetTypeIPv4,
352 }
353
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300354 destinationIP := igmp.GroupAddress
355 if igmp.Version == 3 && igmp.Type == layers.IGMPMembershipReportV3 {
356 //All IGMPv3-capable multicast routers
357 destinationIP = net.ParseIP("224.0.0.22")
358 }
359
Arjun E K57a7fcb2020-01-30 06:44:45 +0000360 ipLayer := &layers.IPv4{
361 Version: 4,
362 TOS: 0x10,
363 Id: 0,
364 TTL: 128,
365 SrcIP: []byte{0, 0, 0, 0},
Baris Ertas0ce2f7c2023-04-11 11:44:24 +0300366 DstIP: destinationIP,
Arjun E K57a7fcb2020-01-30 06:44:45 +0000367 Protocol: layers.IPProtocolIGMP,
368 Options: []layers.IPv4Option{{OptionType: 148, OptionLength: 4, OptionData: make([]byte, 0)}}, //Adding router alert option
369 }
370
371 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, igmp); err != nil {
372 return nil, err
373 }
374
Matteo Scandolo618a6582020-09-09 12:21:29 -0700375 untaggedPkt := gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeEthernet, gopacket.Default)
376 taggedPkt, err := packetHandlers.PushSingleTag(cTag, untaggedPkt, pbit)
377
378 if err != nil {
379 log.Error("TagPacket")
380 return nil, err
381 }
382
383 return taggedPkt.Data(), nil
Arjun E K57a7fcb2020-01-30 06:44:45 +0000384}
385
386//-----------------------------------------***********************---------------------------------
Arjun E K57a7fcb2020-01-30 06:44:45 +0000387
388type IGMP struct {
Matteo Scandolo618a6582020-09-09 12:21:29 -0700389 layers.BaseLayer
390 Type layers.IGMPType
Arjun E K57a7fcb2020-01-30 06:44:45 +0000391 MaxResponseTime time.Duration
392 Checksum uint16
393 GroupAddress net.IP
394 SupressRouterProcessing bool
395 RobustnessValue uint8
396 IntervalTime time.Duration
397 SourceAddresses []net.IP
398 NumberOfGroupRecords uint16
399 NumberOfSources uint16
Baris Ertas53ab13c2023-05-25 16:31:48 +0300400 GroupRecords []layers.IGMPv3GroupRecord
Arjun E K57a7fcb2020-01-30 06:44:45 +0000401 Version uint8 // IGMP protocol version
402}
403
Arjun E Kdd443f02020-02-07 15:24:01 +0000404// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
405type IGMPv3GroupRecord struct {
Anand S Katti09541352020-01-29 15:54:01 +0530406 Type IGMPv3GroupRecordType
407 AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
408 NumberOfSources uint16
409 MulticastAddress net.IP
410 SourceAddresses []net.IP
411 AuxData uint32 // NOT USED
Arjun E Kdd443f02020-02-07 15:24:01 +0000412}
413
414type IGMPv3GroupRecordType uint8
415
416const (
Anand S Katti09541352020-01-29 15:54:01 +0530417 IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
418 IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
419 IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
420 IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
421 IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
422 IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
Arjun E Kdd443f02020-02-07 15:24:01 +0000423)
424
425func (i IGMPv3GroupRecordType) String() string {
Anand S Katti09541352020-01-29 15:54:01 +0530426 switch i {
427 case IGMPIsIn:
428 return "MODE_IS_INCLUDE"
429 case IGMPIsEx:
430 return "MODE_IS_EXCLUDE"
431 case IGMPToIn:
432 return "CHANGE_TO_INCLUDE_MODE"
433 case IGMPToEx:
434 return "CHANGE_TO_EXCLUDE_MODE"
435 case IGMPAllow:
436 return "ALLOW_NEW_SOURCES"
437 case IGMPBlock:
438 return "BLOCK_OLD_SOURCES"
439 default:
440 return ""
441 }
Arjun E Kdd443f02020-02-07 15:24:01 +0000442}
443
Arjun E K57a7fcb2020-01-30 06:44:45 +0000444// SerializeTo writes the serialized form of this layer into the
445// SerializationBuffer, implementing gopacket.SerializableLayer.
446// See the docs for gopacket.SerializableLayer for more info.
Matteo Scandolo618a6582020-09-09 12:21:29 -0700447func (igmp *IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000448 data, err := b.PrependBytes(8915)
Anand S Katti09541352020-01-29 15:54:01 +0530449 if err != nil {
450 return err
451 }
452 if igmp.Version == 2 {
453 data[0] = byte(igmp.Type)
454 data[1] = byte(igmp.MaxResponseTime)
455 data[2] = 0
456 data[3] = 0
457 copy(data[4:8], igmp.GroupAddress.To4())
458 if opts.ComputeChecksums {
459 igmp.Checksum = tcpipChecksum(data, 0)
460 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
461 }
462 } else if igmp.Version == 3 {
Arjun E K57a7fcb2020-01-30 06:44:45 +0000463
Anand S Katti09541352020-01-29 15:54:01 +0530464 data[0] = byte(igmp.Type)
465 data[1] = 0
466 data[2] = 0
467 data[3] = 0
468 data[4] = 0
469 data[5] = 0
470 binary.BigEndian.PutUint16(data[6:8], igmp.NumberOfGroupRecords)
471 j := 8
472 for i := uint16(0); i < igmp.NumberOfGroupRecords; i++ {
473 data[j] = byte(igmp.GroupRecords[i].Type)
474 data[j+1] = byte(0)
475 binary.BigEndian.PutUint16(data[j+2:j+4], igmp.GroupRecords[i].NumberOfSources)
476 copy(data[j+4:j+8], igmp.GroupRecords[i].MulticastAddress.To4())
477 j = j + 8
478 for m := uint16(0); m < igmp.GroupRecords[i].NumberOfSources; m++ {
479 copy(data[j:(j+4)], igmp.GroupRecords[i].SourceAddresses[m].To4())
480 j = j + 4
481 }
482 }
483 if opts.ComputeChecksums {
484 igmp.Checksum = tcpipChecksum(data, 0)
485 binary.BigEndian.PutUint16(data[2:4], igmp.Checksum)
486 }
487 }
488 return nil
Arjun E K57a7fcb2020-01-30 06:44:45 +0000489}
490
491// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
492// initial checksum data that's already been computed.
493func tcpipChecksum(data []byte, csum uint32) uint16 {
494 // to handle odd lengths, we loop to length - 1, incrementing by 2, then
495 // handle the last byte specifically by checking against the original
496 // length.
497 length := len(data) - 1
498 for i := 0; i < length; i += 2 {
499 // For our test packet, doing this manually is about 25% faster
500 // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
501 csum += uint32(data[i]) << 8
502 csum += uint32(data[i+1])
503 }
504 if len(data)%2 == 1 {
505 csum += uint32(data[length]) << 8
506 }
507 for csum > 0xffff {
508 csum = (csum >> 16) + (csum & 0xffff)
509 }
510 return ^uint16(csum)
511}
512
Matteo Scandolo618a6582020-09-09 12:21:29 -0700513func (i *IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }
514func (i *IGMP) LayerContents() []byte { return i.Contents }
515func (i *IGMP) LayerPayload() []byte { return i.Payload }