blob: c71c70aef8aacf1ed7b74ddc41dce25ad0bbe208 [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Brian O'Connor4d084702017-08-03 22:45:58 -07002 * Copyright 2017-present Open Networking Foundation
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;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000020import org.onlab.packet.IGMP.IGMPv2;
21import org.onlab.packet.IGMP.IGMPv3;
ke han81a38b92017-03-10 18:41:44 +080022import org.onlab.packet.IGMPMembership;
23import org.onlab.packet.IGMPQuery;
24import org.onlab.packet.IPv4;
25import org.onlab.packet.Ip4Address;
26import org.onlab.packet.MacAddress;
Esin Karaman586f1d62020-06-04 10:15:34 +000027import org.onlab.packet.VlanId;
ke han81a38b92017-03-10 18:41:44 +080028import org.onosproject.net.DeviceId;
29import org.onosproject.net.PortNumber;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.packet.DefaultOutboundPacket;
33import org.onosproject.net.packet.OutboundPacket;
34import org.onosproject.net.packet.PacketService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000035import org.opencord.igmpproxy.IgmpLeadershipService;
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +030036import org.opencord.igmpproxy.IgmpStatisticType;
developere400c582020-03-24 19:42:08 +010037import org.opencord.igmpproxy.IgmpStatisticsService;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000038import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
ke han81a38b92017-03-10 18:41:44 +080040
41import java.nio.ByteBuffer;
42
43/**
44 * Message encode and send interface for igmpproxy.
45 */
46public final class IgmpSender {
47 static final String V3_REPORT_ADDRESS = "224.0.0.22";
Arjun E Kb0018fd2020-04-07 13:26:40 +000048 static final String V2_LEAVE_DST = "224.0.0.2";
ke han81a38b92017-03-10 18:41:44 +080049 static final String MAC_ADDRESS = "DE:AD:BE:EF:BA:11";
50 static final short DEFAULT_MVLAN = 4000;
51 static final byte DEFAULT_COS = 7;
52 static final int DEFAULT_MEX_RESP = 10;
53 static final byte[] RA_BYTES = {(byte) 0x94, (byte) 0x04, (byte) 0x00, (byte) 0x00};
54
55 private static IgmpSender instance = null;
56 private PacketService packetService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000057 private IgmpLeadershipService igmpLeadershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000058 private IgmpStatisticsService igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080059 private boolean withRAUplink = true;
60 private boolean withRADownlink = false;
61 private short mvlan = DEFAULT_MVLAN;
Esin Karaman586f1d62020-06-04 10:15:34 +000062 private short mvlanInner = VlanId.NONE.toShort();
ke han81a38b92017-03-10 18:41:44 +080063 private byte igmpCos = DEFAULT_COS;
Esin Karaman586f1d62020-06-04 10:15:34 +000064 private byte igmpUniCos = DEFAULT_COS;
ke han81a38b92017-03-10 18:41:44 +080065 private int maxResp = DEFAULT_MEX_RESP;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000066 private Logger log = LoggerFactory.getLogger(getClass());
ke han81a38b92017-03-10 18:41:44 +080067
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000068 private IgmpSender(PacketService packetService, IgmpLeadershipService igmpLeadershipService,
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000069 IgmpStatisticsService igmpStatisticsService) {
ke han81a38b92017-03-10 18:41:44 +080070 this.packetService = packetService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000071 this.igmpLeadershipService = igmpLeadershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000072 this.igmpStatisticsService = igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080073 }
74
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000075 public static void init(PacketService packetService, IgmpLeadershipService igmpLeadershipService,
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000076 IgmpStatisticsService igmpStatisticsService) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000077 instance = new IgmpSender(packetService, igmpLeadershipService, igmpStatisticsService);
ke han81a38b92017-03-10 18:41:44 +080078 }
79
80 public static IgmpSender getInstance() {
81 return instance;
82 }
83
84 public void setWithRAUplink(boolean withRaUplink) {
85 this.withRAUplink = withRaUplink;
86 }
87
88 public void setWithRADownlink(boolean withRADownlink) {
89 this.withRADownlink = withRADownlink;
90 }
91
92 public void setMvlan(short mvlan) {
93 this.mvlan = mvlan;
94 }
95
Esin Karaman586f1d62020-06-04 10:15:34 +000096 public void setMvlanInner(short mvlanInner) {
97 this.mvlanInner = mvlanInner;
98 }
99
ke han81a38b92017-03-10 18:41:44 +0800100 public void setIgmpCos(byte igmpCos) {
101 this.igmpCos = igmpCos;
102 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000103 public void setIgmpUniCos(byte igmpUniCos) {
104 this.igmpUniCos = igmpUniCos;
105 }
ke han81a38b92017-03-10 18:41:44 +0800106
107 public void setMaxResp(int maxResp) {
108 this.maxResp = maxResp;
109 }
110
111 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
112 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
113 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
114
Esin Karaman586f1d62020-06-04 10:15:34 +0000115 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
116 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800117 }
118
Arjun E Kb0018fd2020-04-07 13:26:40 +0000119 public Ethernet buildIgmpV2Join(Ip4Address groupIp, Ip4Address sourceIp) {
120 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000121 return buildIgmpPacket(IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT, groupIp, igmpMembership,
122 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000123 }
124
125 public Ethernet buildIgmpV2ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
126 return buildIgmpV2Join(groupIp, sourceIp);
127 }
128
ke han81a38b92017-03-10 18:41:44 +0800129 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
130 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
131 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
132
Esin Karaman586f1d62020-06-04 10:15:34 +0000133 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
134 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800135 }
136
137 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
138 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
139 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
140
Esin Karaman586f1d62020-06-04 10:15:34 +0000141 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
142 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800143 }
144
Arjun E Kb0018fd2020-04-07 13:26:40 +0000145 public Ethernet buildIgmpV2Leave(Ip4Address groupIp, Ip4Address sourceIp) {
146 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000147 return buildIgmpPacket(IGMP.TYPE_IGMPV2_LEAVE_GROUP, groupIp, igmpMembership,
148 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000149 }
150
Esin Karaman586f1d62020-06-04 10:15:34 +0000151 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
152 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
153 sourceIp, true, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800154 }
155
Esin Karaman586f1d62020-06-04 10:15:34 +0000156 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
157 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
158 sourceIp, false, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800159 }
160
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000161 protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
Esin Karaman586f1d62020-06-04 10:15:34 +0000162 Ip4Address sourceIp, boolean isV2Query, short vlan,
163 short innerVlan, byte igmpCos) {
ke han81a38b92017-03-10 18:41:44 +0800164
165 IGMP igmpPacket;
166 if (isV2Query) {
167 igmpPacket = new IGMP.IGMPv2();
168 } else {
169 igmpPacket = new IGMP.IGMPv3();
170 }
171
172 IPv4 ip4Packet = new IPv4();
173 Ethernet ethPkt = new Ethernet();
Esin Karaman45d2d6a2020-05-04 12:27:55 +0000174 ethPkt.setPad(true);
ke han81a38b92017-03-10 18:41:44 +0800175
176 igmpPacket.setIgmpType(type);
177
178 switch (type) {
179 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
180 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
181 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
182
183 igmpPacket.addGroup(igmpQuery);
184 ip4Packet.setDestinationAddress(groupIp.toInt());
185 if (withRADownlink) {
186 ip4Packet.setOptions(RA_BYTES);
187 }
188 break;
189
190 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
191 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000192 log.debug("Igmp membership is not found. igmp-type {} ", type);
ke han81a38b92017-03-10 18:41:44 +0800193 return null;
194 }
195 igmpPacket.addGroup(igmpMembership);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000196 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
197
ke han81a38b92017-03-10 18:41:44 +0800198 if (withRAUplink) {
199 ip4Packet.setOptions(RA_BYTES);
200 }
201 break;
202
203 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
204 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Arjun E Kb0018fd2020-04-07 13:26:40 +0000205 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000206 log.debug("Igmp membership is not found. igmp-type {} ", type);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000207 return null;
208 }
209 igmpPacket.addGroup(igmpMembership);
210 int dst = (type == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
211 groupIp.toInt() :
212 Ip4Address.valueOf(V2_LEAVE_DST).toInt());
213 ip4Packet.setDestinationAddress(dst);
214 break;
ke han81a38b92017-03-10 18:41:44 +0800215 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000216 log.debug("Unknown igmp type: {} ", type);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300217 igmpStatisticsService.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800218 return null;
219 }
220
221 igmpPacket.setParent(ip4Packet);
222 ip4Packet.setSourceAddress(sourceIp.toInt());
223 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
224 ip4Packet.setPayload(igmpPacket);
225 ip4Packet.setParent(ethPkt);
226 ip4Packet.setTtl((byte) 0x78);
227
228 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
229 ethPkt.setSourceMACAddress(MAC_ADDRESS);
230 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
231 ethPkt.setPayload(ip4Packet);
Esin Karaman586f1d62020-06-04 10:15:34 +0000232 ethPkt.setVlanID(vlan);
ke han81a38b92017-03-10 18:41:44 +0800233 ethPkt.setPriorityCode(igmpCos);
234
Esin Karaman586f1d62020-06-04 10:15:34 +0000235 if (innerVlan != VlanId.NONE.toShort()) {
236 ethPkt.setQinQTPID(Ethernet.TYPE_VLAN);
237 ethPkt.setQinQVID(vlan);
238 ethPkt.setVlanID(innerVlan);
239 ethPkt.setQinQPriorityCode(igmpCos);
240 }
241
ke han81a38b92017-03-10 18:41:44 +0800242 return ethPkt;
243 }
244
245 private MacAddress multiaddToMac(int multiaddress) {
246 byte[] b = new byte[3];
247 b[0] = (byte) (multiaddress & 0xff);
248 b[1] = (byte) (multiaddress >> 8 & 0xff);
249 b[2] = (byte) (multiaddress >> 16 & 0x7f);
250 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
251
252 MacAddress mac = MacAddress.valueOf(macByte);
253 return mac;
254 }
255
Esin Karaman00e16b72020-02-21 10:32:39 +0000256 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000257 if (!igmpLeadershipService.isLocalLeader(deviceId)) {
ke han81a38b92017-03-10 18:41:44 +0800258 return;
259 }
260
ke han81a38b92017-03-10 18:41:44 +0800261 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000262 if (IgmpManager.connectPoint == null) {
263 log.warn("cannot find a connectPoint to send the packet uplink");
264 return;
265 }
ke han81a38b92017-03-10 18:41:44 +0800266 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
267 } else {
Esin Karaman00e16b72020-02-21 10:32:39 +0000268 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800269 }
270 }
271
272 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000273 if (!igmpLeadershipService.isLocalLeader(deviceId)) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200274 log.trace("Instance is not leader for device {}, " +
275 "not emitting IMGP packet on port {}", deviceId, portNumber);
ke han81a38b92017-03-10 18:41:44 +0800276 return;
277 }
278
Andrea Campanella2c70a572020-06-05 13:31:45 +0200279 if (log.isTraceEnabled()) {
280 log.trace("Emitting on {}/{} outbound IGMP packet {}", deviceId, portNumber, ethPkt);
281 }
282
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000283 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
284 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
285 // We are checking the length of packets. Right now the counter value will be 0 because of internal translation
286 // As packet length will always be valid
287 // This counter will be useful in future if we change the procedure to generate the packets.
288 if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
289 || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.serialize().length < IGMPv2.HEADER_LENGTH) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300290 igmpStatisticsService.increaseStat(IgmpStatisticType.INVALID_IGMP_LENGTH);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000291 } else if (igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
292 && igmp.serialize().length < IGMPv3.MINIMUM_HEADER_LEN) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300293 igmpStatisticsService.increaseStat(IgmpStatisticType.INVALID_IGMP_LENGTH);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000294 }
ke han81a38b92017-03-10 18:41:44 +0800295 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
296 .setOutput(portNumber).build();
297 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
298 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300299 igmpStatisticsService.increaseStat(IgmpStatisticType.VALID_IGMP_PACKET_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800300 packetService.emit(packet);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200301 if (log.isTraceEnabled()) {
302 log.trace("Emitted on {}/{} outbound IGMP packet {}", deviceId, portNumber, packet);
303 }
ke han81a38b92017-03-10 18:41:44 +0800304
305 }
306}