blob: f42c7946279d2c03c70d0d16869f4125544733b7 [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Joey Armstrong517afab2023-01-09 14:22:22 -05002 * Copyright 2017-2023 Open Networking Foundation (ONF) and the ONF Contributors
David K. Bainbridged77028f2017-08-01 12:47:55 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
developere400c582020-03-24 19:42:08 +010016package org.opencord.igmpproxy.impl;
ke han81a38b92017-03-10 18:41:44 +080017
18import org.onlab.packet.Ethernet;
19import org.onlab.packet.IGMP;
20import org.onlab.packet.IGMPMembership;
21import org.onlab.packet.IGMPQuery;
22import org.onlab.packet.IPv4;
23import org.onlab.packet.Ip4Address;
24import org.onlab.packet.MacAddress;
Esin Karaman586f1d62020-06-04 10:15:34 +000025import org.onlab.packet.VlanId;
ke han81a38b92017-03-10 18:41:44 +080026import org.onosproject.net.DeviceId;
27import org.onosproject.net.PortNumber;
28import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.packet.DefaultOutboundPacket;
31import org.onosproject.net.packet.OutboundPacket;
32import org.onosproject.net.packet.PacketService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000033import org.opencord.igmpproxy.IgmpLeadershipService;
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +030034import org.opencord.igmpproxy.IgmpStatisticType;
developere400c582020-03-24 19:42:08 +010035import org.opencord.igmpproxy.IgmpStatisticsService;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
ke han81a38b92017-03-10 18:41:44 +080038
39import java.nio.ByteBuffer;
40
41/**
42 * Message encode and send interface for igmpproxy.
43 */
44public final class IgmpSender {
45 static final String V3_REPORT_ADDRESS = "224.0.0.22";
Arjun E Kb0018fd2020-04-07 13:26:40 +000046 static final String V2_LEAVE_DST = "224.0.0.2";
ke han81a38b92017-03-10 18:41:44 +080047 static final String MAC_ADDRESS = "DE:AD:BE:EF:BA:11";
48 static final short DEFAULT_MVLAN = 4000;
49 static final byte DEFAULT_COS = 7;
50 static final int DEFAULT_MEX_RESP = 10;
51 static final byte[] RA_BYTES = {(byte) 0x94, (byte) 0x04, (byte) 0x00, (byte) 0x00};
52
53 private static IgmpSender instance = null;
54 private PacketService packetService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000055 private IgmpLeadershipService igmpLeadershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000056 private IgmpStatisticsService igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080057 private boolean withRAUplink = true;
58 private boolean withRADownlink = false;
59 private short mvlan = DEFAULT_MVLAN;
Esin Karaman586f1d62020-06-04 10:15:34 +000060 private short mvlanInner = VlanId.NONE.toShort();
ke han81a38b92017-03-10 18:41:44 +080061 private byte igmpCos = DEFAULT_COS;
Esin Karaman586f1d62020-06-04 10:15:34 +000062 private byte igmpUniCos = DEFAULT_COS;
ke han81a38b92017-03-10 18:41:44 +080063 private int maxResp = DEFAULT_MEX_RESP;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000064 private Logger log = LoggerFactory.getLogger(getClass());
ke han81a38b92017-03-10 18:41:44 +080065
Esin Karaman4a9075d2020-07-14 14:46:14 +000066 private IgmpSender(PacketService packetService, IgmpStatisticsService igmpStatisticsService) {
ke han81a38b92017-03-10 18:41:44 +080067 this.packetService = packetService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000068 this.igmpStatisticsService = igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080069 }
70
Esin Karaman4a9075d2020-07-14 14:46:14 +000071 public static void init(PacketService packetService, IgmpStatisticsService igmpStatisticsService) {
72 instance = new IgmpSender(packetService, igmpStatisticsService);
ke han81a38b92017-03-10 18:41:44 +080073 }
74
75 public static IgmpSender getInstance() {
76 return instance;
77 }
78
79 public void setWithRAUplink(boolean withRaUplink) {
80 this.withRAUplink = withRaUplink;
81 }
82
83 public void setWithRADownlink(boolean withRADownlink) {
84 this.withRADownlink = withRADownlink;
85 }
86
87 public void setMvlan(short mvlan) {
88 this.mvlan = mvlan;
89 }
90
Esin Karaman586f1d62020-06-04 10:15:34 +000091 public void setMvlanInner(short mvlanInner) {
92 this.mvlanInner = mvlanInner;
93 }
94
ke han81a38b92017-03-10 18:41:44 +080095 public void setIgmpCos(byte igmpCos) {
96 this.igmpCos = igmpCos;
97 }
Esin Karaman586f1d62020-06-04 10:15:34 +000098 public void setIgmpUniCos(byte igmpUniCos) {
99 this.igmpUniCos = igmpUniCos;
100 }
ke han81a38b92017-03-10 18:41:44 +0800101
102 public void setMaxResp(int maxResp) {
103 this.maxResp = maxResp;
104 }
105
106 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
107 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
108 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
109
Esin Karaman586f1d62020-06-04 10:15:34 +0000110 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
111 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800112 }
113
Arjun E Kb0018fd2020-04-07 13:26:40 +0000114 public Ethernet buildIgmpV2Join(Ip4Address groupIp, Ip4Address sourceIp) {
115 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000116 return buildIgmpPacket(IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT, groupIp, igmpMembership,
117 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000118 }
119
120 public Ethernet buildIgmpV2ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
121 return buildIgmpV2Join(groupIp, sourceIp);
122 }
123
ke han81a38b92017-03-10 18:41:44 +0800124 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
125 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
126 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
127
Esin Karaman586f1d62020-06-04 10:15:34 +0000128 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
129 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800130 }
131
132 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
133 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
134 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
135
Esin Karaman586f1d62020-06-04 10:15:34 +0000136 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
137 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800138 }
139
Arjun E Kb0018fd2020-04-07 13:26:40 +0000140 public Ethernet buildIgmpV2Leave(Ip4Address groupIp, Ip4Address sourceIp) {
141 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000142 return buildIgmpPacket(IGMP.TYPE_IGMPV2_LEAVE_GROUP, groupIp, igmpMembership,
143 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000144 }
145
Esin Karaman586f1d62020-06-04 10:15:34 +0000146 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
147 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
148 sourceIp, true, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800149 }
150
Esin Karaman586f1d62020-06-04 10:15:34 +0000151 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
152 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
153 sourceIp, false, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800154 }
155
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000156 protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
Esin Karaman586f1d62020-06-04 10:15:34 +0000157 Ip4Address sourceIp, boolean isV2Query, short vlan,
158 short innerVlan, byte igmpCos) {
ke han81a38b92017-03-10 18:41:44 +0800159
160 IGMP igmpPacket;
161 if (isV2Query) {
162 igmpPacket = new IGMP.IGMPv2();
163 } else {
164 igmpPacket = new IGMP.IGMPv3();
165 }
166
167 IPv4 ip4Packet = new IPv4();
168 Ethernet ethPkt = new Ethernet();
Esin Karaman45d2d6a2020-05-04 12:27:55 +0000169 ethPkt.setPad(true);
ke han81a38b92017-03-10 18:41:44 +0800170
171 igmpPacket.setIgmpType(type);
172
173 switch (type) {
174 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
175 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
176 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
177
178 igmpPacket.addGroup(igmpQuery);
179 ip4Packet.setDestinationAddress(groupIp.toInt());
180 if (withRADownlink) {
181 ip4Packet.setOptions(RA_BYTES);
182 }
183 break;
184
185 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
186 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000187 log.debug("Igmp membership is not found. igmp-type {} ", type);
ke han81a38b92017-03-10 18:41:44 +0800188 return null;
189 }
190 igmpPacket.addGroup(igmpMembership);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000191 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
192
ke han81a38b92017-03-10 18:41:44 +0800193 if (withRAUplink) {
194 ip4Packet.setOptions(RA_BYTES);
195 }
196 break;
197
198 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
199 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Arjun E Kb0018fd2020-04-07 13:26:40 +0000200 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000201 log.debug("Igmp membership is not found. igmp-type {} ", type);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000202 return null;
203 }
204 igmpPacket.addGroup(igmpMembership);
205 int dst = (type == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
206 groupIp.toInt() :
207 Ip4Address.valueOf(V2_LEAVE_DST).toInt());
208 ip4Packet.setDestinationAddress(dst);
209 break;
ke han81a38b92017-03-10 18:41:44 +0800210 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000211 log.debug("Unknown igmp type: {} ", type);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300212 igmpStatisticsService.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800213 return null;
214 }
215
216 igmpPacket.setParent(ip4Packet);
217 ip4Packet.setSourceAddress(sourceIp.toInt());
218 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
219 ip4Packet.setPayload(igmpPacket);
220 ip4Packet.setParent(ethPkt);
221 ip4Packet.setTtl((byte) 0x78);
222
223 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
224 ethPkt.setSourceMACAddress(MAC_ADDRESS);
225 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
226 ethPkt.setPayload(ip4Packet);
Esin Karaman586f1d62020-06-04 10:15:34 +0000227 ethPkt.setVlanID(vlan);
ke han81a38b92017-03-10 18:41:44 +0800228 ethPkt.setPriorityCode(igmpCos);
229
Esin Karaman586f1d62020-06-04 10:15:34 +0000230 if (innerVlan != VlanId.NONE.toShort()) {
231 ethPkt.setQinQTPID(Ethernet.TYPE_VLAN);
232 ethPkt.setQinQVID(vlan);
233 ethPkt.setVlanID(innerVlan);
234 ethPkt.setQinQPriorityCode(igmpCos);
235 }
236
ke han81a38b92017-03-10 18:41:44 +0800237 return ethPkt;
238 }
239
240 private MacAddress multiaddToMac(int multiaddress) {
241 byte[] b = new byte[3];
242 b[0] = (byte) (multiaddress & 0xff);
243 b[1] = (byte) (multiaddress >> 8 & 0xff);
244 b[2] = (byte) (multiaddress >> 16 & 0x7f);
245 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
246
247 MacAddress mac = MacAddress.valueOf(macByte);
248 return mac;
249 }
250
Esin Karaman00e16b72020-02-21 10:32:39 +0000251 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
ke han81a38b92017-03-10 18:41:44 +0800252 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000253 if (IgmpManager.connectPoint == null) {
254 log.warn("cannot find a connectPoint to send the packet uplink");
255 return;
256 }
ke han81a38b92017-03-10 18:41:44 +0800257 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
258 } else {
Esin Karaman00e16b72020-02-21 10:32:39 +0000259 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800260 }
261 }
262
263 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200264 if (log.isTraceEnabled()) {
265 log.trace("Emitting on {}/{} outbound IGMP packet {}", deviceId, portNumber, ethPkt);
266 }
267
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000268 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
269 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Ilayda Ozdemir37e83412020-12-19 14:54:27 +0000270 // We are checking the groups.
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000271 if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
Ilayda Ozdemir37e83412020-12-19 14:54:27 +0000272 || igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
273 || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.getGroups().isEmpty()) {
274 igmpStatisticsService.increaseStat(IgmpStatisticType.INVALID_IGMP_LENGTH);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000275 }
ke han81a38b92017-03-10 18:41:44 +0800276 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
277 .setOutput(portNumber).build();
278 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
279 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300280 igmpStatisticsService.increaseStat(IgmpStatisticType.VALID_IGMP_PACKET_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800281 packetService.emit(packet);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200282 if (log.isTraceEnabled()) {
283 log.trace("Emitted on {}/{} outbound IGMP packet {}", deviceId, portNumber, packet);
284 }
ke han81a38b92017-03-10 18:41:44 +0800285
286 }
287}