blob: 79aa80ee886137e3d874a20dd654db015f6d52ae [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
Esin Karaman4a9075d2020-07-14 14:46:14 +000068 private IgmpSender(PacketService packetService, IgmpStatisticsService igmpStatisticsService) {
ke han81a38b92017-03-10 18:41:44 +080069 this.packetService = packetService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000070 this.igmpStatisticsService = igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080071 }
72
Esin Karaman4a9075d2020-07-14 14:46:14 +000073 public static void init(PacketService packetService, IgmpStatisticsService igmpStatisticsService) {
74 instance = new IgmpSender(packetService, igmpStatisticsService);
ke han81a38b92017-03-10 18:41:44 +080075 }
76
77 public static IgmpSender getInstance() {
78 return instance;
79 }
80
81 public void setWithRAUplink(boolean withRaUplink) {
82 this.withRAUplink = withRaUplink;
83 }
84
85 public void setWithRADownlink(boolean withRADownlink) {
86 this.withRADownlink = withRADownlink;
87 }
88
89 public void setMvlan(short mvlan) {
90 this.mvlan = mvlan;
91 }
92
Esin Karaman586f1d62020-06-04 10:15:34 +000093 public void setMvlanInner(short mvlanInner) {
94 this.mvlanInner = mvlanInner;
95 }
96
ke han81a38b92017-03-10 18:41:44 +080097 public void setIgmpCos(byte igmpCos) {
98 this.igmpCos = igmpCos;
99 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000100 public void setIgmpUniCos(byte igmpUniCos) {
101 this.igmpUniCos = igmpUniCos;
102 }
ke han81a38b92017-03-10 18:41:44 +0800103
104 public void setMaxResp(int maxResp) {
105 this.maxResp = maxResp;
106 }
107
108 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
109 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
110 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
111
Esin Karaman586f1d62020-06-04 10:15:34 +0000112 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
113 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800114 }
115
Arjun E Kb0018fd2020-04-07 13:26:40 +0000116 public Ethernet buildIgmpV2Join(Ip4Address groupIp, Ip4Address sourceIp) {
117 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000118 return buildIgmpPacket(IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT, groupIp, igmpMembership,
119 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000120 }
121
122 public Ethernet buildIgmpV2ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
123 return buildIgmpV2Join(groupIp, sourceIp);
124 }
125
ke han81a38b92017-03-10 18:41:44 +0800126 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
127 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
128 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
129
Esin Karaman586f1d62020-06-04 10:15:34 +0000130 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
131 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800132 }
133
134 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
135 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
136 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
137
Esin Karaman586f1d62020-06-04 10:15:34 +0000138 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
139 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800140 }
141
Arjun E Kb0018fd2020-04-07 13:26:40 +0000142 public Ethernet buildIgmpV2Leave(Ip4Address groupIp, Ip4Address sourceIp) {
143 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000144 return buildIgmpPacket(IGMP.TYPE_IGMPV2_LEAVE_GROUP, groupIp, igmpMembership,
145 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000146 }
147
Esin Karaman586f1d62020-06-04 10:15:34 +0000148 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
149 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
150 sourceIp, true, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800151 }
152
Esin Karaman586f1d62020-06-04 10:15:34 +0000153 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
154 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
155 sourceIp, false, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800156 }
157
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000158 protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
Esin Karaman586f1d62020-06-04 10:15:34 +0000159 Ip4Address sourceIp, boolean isV2Query, short vlan,
160 short innerVlan, byte igmpCos) {
ke han81a38b92017-03-10 18:41:44 +0800161
162 IGMP igmpPacket;
163 if (isV2Query) {
164 igmpPacket = new IGMP.IGMPv2();
165 } else {
166 igmpPacket = new IGMP.IGMPv3();
167 }
168
169 IPv4 ip4Packet = new IPv4();
170 Ethernet ethPkt = new Ethernet();
Esin Karaman45d2d6a2020-05-04 12:27:55 +0000171 ethPkt.setPad(true);
ke han81a38b92017-03-10 18:41:44 +0800172
173 igmpPacket.setIgmpType(type);
174
175 switch (type) {
176 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
177 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
178 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
179
180 igmpPacket.addGroup(igmpQuery);
181 ip4Packet.setDestinationAddress(groupIp.toInt());
182 if (withRADownlink) {
183 ip4Packet.setOptions(RA_BYTES);
184 }
185 break;
186
187 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
188 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000189 log.debug("Igmp membership is not found. igmp-type {} ", type);
ke han81a38b92017-03-10 18:41:44 +0800190 return null;
191 }
192 igmpPacket.addGroup(igmpMembership);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000193 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
194
ke han81a38b92017-03-10 18:41:44 +0800195 if (withRAUplink) {
196 ip4Packet.setOptions(RA_BYTES);
197 }
198 break;
199
200 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
201 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Arjun E Kb0018fd2020-04-07 13:26:40 +0000202 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000203 log.debug("Igmp membership is not found. igmp-type {} ", type);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000204 return null;
205 }
206 igmpPacket.addGroup(igmpMembership);
207 int dst = (type == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
208 groupIp.toInt() :
209 Ip4Address.valueOf(V2_LEAVE_DST).toInt());
210 ip4Packet.setDestinationAddress(dst);
211 break;
ke han81a38b92017-03-10 18:41:44 +0800212 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000213 log.debug("Unknown igmp type: {} ", type);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300214 igmpStatisticsService.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800215 return null;
216 }
217
218 igmpPacket.setParent(ip4Packet);
219 ip4Packet.setSourceAddress(sourceIp.toInt());
220 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
221 ip4Packet.setPayload(igmpPacket);
222 ip4Packet.setParent(ethPkt);
223 ip4Packet.setTtl((byte) 0x78);
224
225 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
226 ethPkt.setSourceMACAddress(MAC_ADDRESS);
227 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
228 ethPkt.setPayload(ip4Packet);
Esin Karaman586f1d62020-06-04 10:15:34 +0000229 ethPkt.setVlanID(vlan);
ke han81a38b92017-03-10 18:41:44 +0800230 ethPkt.setPriorityCode(igmpCos);
231
Esin Karaman586f1d62020-06-04 10:15:34 +0000232 if (innerVlan != VlanId.NONE.toShort()) {
233 ethPkt.setQinQTPID(Ethernet.TYPE_VLAN);
234 ethPkt.setQinQVID(vlan);
235 ethPkt.setVlanID(innerVlan);
236 ethPkt.setQinQPriorityCode(igmpCos);
237 }
238
ke han81a38b92017-03-10 18:41:44 +0800239 return ethPkt;
240 }
241
242 private MacAddress multiaddToMac(int multiaddress) {
243 byte[] b = new byte[3];
244 b[0] = (byte) (multiaddress & 0xff);
245 b[1] = (byte) (multiaddress >> 8 & 0xff);
246 b[2] = (byte) (multiaddress >> 16 & 0x7f);
247 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
248
249 MacAddress mac = MacAddress.valueOf(macByte);
250 return mac;
251 }
252
Esin Karaman00e16b72020-02-21 10:32:39 +0000253 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
ke han81a38b92017-03-10 18:41:44 +0800254 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000255 if (IgmpManager.connectPoint == null) {
256 log.warn("cannot find a connectPoint to send the packet uplink");
257 return;
258 }
ke han81a38b92017-03-10 18:41:44 +0800259 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
260 } else {
Esin Karaman00e16b72020-02-21 10:32:39 +0000261 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800262 }
263 }
264
265 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200266 if (log.isTraceEnabled()) {
267 log.trace("Emitting on {}/{} outbound IGMP packet {}", deviceId, portNumber, ethPkt);
268 }
269
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000270 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
271 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
272 // We are checking the length of packets. Right now the counter value will be 0 because of internal translation
273 // As packet length will always be valid
274 // This counter will be useful in future if we change the procedure to generate the packets.
275 if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
276 || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.serialize().length < IGMPv2.HEADER_LENGTH) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300277 igmpStatisticsService.increaseStat(IgmpStatisticType.INVALID_IGMP_LENGTH);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000278 } else if (igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
279 && igmp.serialize().length < IGMPv3.MINIMUM_HEADER_LEN) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300280 igmpStatisticsService.increaseStat(IgmpStatisticType.INVALID_IGMP_LENGTH);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000281 }
ke han81a38b92017-03-10 18:41:44 +0800282 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
283 .setOutput(portNumber).build();
284 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
285 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300286 igmpStatisticsService.increaseStat(IgmpStatisticType.VALID_IGMP_PACKET_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800287 packetService.emit(packet);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200288 if (log.isTraceEnabled()) {
289 log.trace("Emitted on {}/{} outbound IGMP packet {}", deviceId, portNumber, packet);
290 }
ke han81a38b92017-03-10 18:41:44 +0800291
292 }
293}