blob: e6584adca17ed9f7e336856abdb59e0d71648ee8 [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;
developere400c582020-03-24 19:42:08 +010036import org.opencord.igmpproxy.IgmpStatisticsService;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
ke han81a38b92017-03-10 18:41:44 +080039
40import java.nio.ByteBuffer;
41
42/**
43 * Message encode and send interface for igmpproxy.
44 */
45public final class IgmpSender {
46 static final String V3_REPORT_ADDRESS = "224.0.0.22";
Arjun E Kb0018fd2020-04-07 13:26:40 +000047 static final String V2_LEAVE_DST = "224.0.0.2";
ke han81a38b92017-03-10 18:41:44 +080048 static final String MAC_ADDRESS = "DE:AD:BE:EF:BA:11";
49 static final short DEFAULT_MVLAN = 4000;
50 static final byte DEFAULT_COS = 7;
51 static final int DEFAULT_MEX_RESP = 10;
52 static final byte[] RA_BYTES = {(byte) 0x94, (byte) 0x04, (byte) 0x00, (byte) 0x00};
53
54 private static IgmpSender instance = null;
55 private PacketService packetService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000056 private IgmpLeadershipService igmpLeadershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000057 private IgmpStatisticsService igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080058 private boolean withRAUplink = true;
59 private boolean withRADownlink = false;
60 private short mvlan = DEFAULT_MVLAN;
Esin Karaman586f1d62020-06-04 10:15:34 +000061 private short mvlanInner = VlanId.NONE.toShort();
ke han81a38b92017-03-10 18:41:44 +080062 private byte igmpCos = DEFAULT_COS;
Esin Karaman586f1d62020-06-04 10:15:34 +000063 private byte igmpUniCos = DEFAULT_COS;
ke han81a38b92017-03-10 18:41:44 +080064 private int maxResp = DEFAULT_MEX_RESP;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000065 private Logger log = LoggerFactory.getLogger(getClass());
ke han81a38b92017-03-10 18:41:44 +080066
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000067 private IgmpSender(PacketService packetService, IgmpLeadershipService igmpLeadershipService,
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000068 IgmpStatisticsService igmpStatisticsService) {
ke han81a38b92017-03-10 18:41:44 +080069 this.packetService = packetService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000070 this.igmpLeadershipService = igmpLeadershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000071 this.igmpStatisticsService = igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080072 }
73
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000074 public static void init(PacketService packetService, IgmpLeadershipService igmpLeadershipService,
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000075 IgmpStatisticsService igmpStatisticsService) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000076 instance = new IgmpSender(packetService, igmpLeadershipService, igmpStatisticsService);
ke han81a38b92017-03-10 18:41:44 +080077 }
78
79 public static IgmpSender getInstance() {
80 return instance;
81 }
82
83 public void setWithRAUplink(boolean withRaUplink) {
84 this.withRAUplink = withRaUplink;
85 }
86
87 public void setWithRADownlink(boolean withRADownlink) {
88 this.withRADownlink = withRADownlink;
89 }
90
91 public void setMvlan(short mvlan) {
92 this.mvlan = mvlan;
93 }
94
Esin Karaman586f1d62020-06-04 10:15:34 +000095 public void setMvlanInner(short mvlanInner) {
96 this.mvlanInner = mvlanInner;
97 }
98
ke han81a38b92017-03-10 18:41:44 +080099 public void setIgmpCos(byte igmpCos) {
100 this.igmpCos = igmpCos;
101 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000102 public void setIgmpUniCos(byte igmpUniCos) {
103 this.igmpUniCos = igmpUniCos;
104 }
ke han81a38b92017-03-10 18:41:44 +0800105
106 public void setMaxResp(int maxResp) {
107 this.maxResp = maxResp;
108 }
109
110 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
111 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
112 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
113
Esin Karaman586f1d62020-06-04 10:15:34 +0000114 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
115 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800116 }
117
Arjun E Kb0018fd2020-04-07 13:26:40 +0000118 public Ethernet buildIgmpV2Join(Ip4Address groupIp, Ip4Address sourceIp) {
119 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000120 return buildIgmpPacket(IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT, groupIp, igmpMembership,
121 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000122 }
123
124 public Ethernet buildIgmpV2ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
125 return buildIgmpV2Join(groupIp, sourceIp);
126 }
127
ke han81a38b92017-03-10 18:41:44 +0800128 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
129 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
130 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
131
Esin Karaman586f1d62020-06-04 10:15:34 +0000132 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
133 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800134 }
135
136 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
137 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
138 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
139
Esin Karaman586f1d62020-06-04 10:15:34 +0000140 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership,
141 sourceIp, false, mvlan, mvlanInner, igmpCos);
ke han81a38b92017-03-10 18:41:44 +0800142 }
143
Arjun E Kb0018fd2020-04-07 13:26:40 +0000144 public Ethernet buildIgmpV2Leave(Ip4Address groupIp, Ip4Address sourceIp) {
145 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
Esin Karaman586f1d62020-06-04 10:15:34 +0000146 return buildIgmpPacket(IGMP.TYPE_IGMPV2_LEAVE_GROUP, groupIp, igmpMembership,
147 sourceIp, true, mvlan, mvlanInner, igmpCos);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000148 }
149
Esin Karaman586f1d62020-06-04 10:15:34 +0000150 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
151 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
152 sourceIp, true, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800153 }
154
Esin Karaman586f1d62020-06-04 10:15:34 +0000155 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp, short vlan) {
156 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null,
157 sourceIp, false, vlan, VlanId.NONE.toShort(), igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800158 }
159
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000160 protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
Esin Karaman586f1d62020-06-04 10:15:34 +0000161 Ip4Address sourceIp, boolean isV2Query, short vlan,
162 short innerVlan, byte igmpCos) {
ke han81a38b92017-03-10 18:41:44 +0800163
164 IGMP igmpPacket;
165 if (isV2Query) {
166 igmpPacket = new IGMP.IGMPv2();
167 } else {
168 igmpPacket = new IGMP.IGMPv3();
169 }
170
171 IPv4 ip4Packet = new IPv4();
172 Ethernet ethPkt = new Ethernet();
Esin Karaman45d2d6a2020-05-04 12:27:55 +0000173 ethPkt.setPad(true);
ke han81a38b92017-03-10 18:41:44 +0800174
175 igmpPacket.setIgmpType(type);
176
177 switch (type) {
178 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
179 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
180 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
181
182 igmpPacket.addGroup(igmpQuery);
183 ip4Packet.setDestinationAddress(groupIp.toInt());
184 if (withRADownlink) {
185 ip4Packet.setOptions(RA_BYTES);
186 }
187 break;
188
189 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
190 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000191 log.debug("Igmp membership is not found. igmp-type {} ", type);
ke han81a38b92017-03-10 18:41:44 +0800192 return null;
193 }
194 igmpPacket.addGroup(igmpMembership);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000195 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
196
ke han81a38b92017-03-10 18:41:44 +0800197 if (withRAUplink) {
198 ip4Packet.setOptions(RA_BYTES);
199 }
200 break;
201
202 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
203 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Arjun E Kb0018fd2020-04-07 13:26:40 +0000204 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000205 log.debug("Igmp membership is not found. igmp-type {} ", type);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000206 return null;
207 }
208 igmpPacket.addGroup(igmpMembership);
209 int dst = (type == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
210 groupIp.toInt() :
211 Ip4Address.valueOf(V2_LEAVE_DST).toInt());
212 ip4Packet.setDestinationAddress(dst);
213 break;
ke han81a38b92017-03-10 18:41:44 +0800214 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000215 log.debug("Unknown igmp type: {} ", type);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000216 igmpStatisticsService.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
ke han81a38b92017-03-10 18:41:44 +0800217 return null;
218 }
219
220 igmpPacket.setParent(ip4Packet);
221 ip4Packet.setSourceAddress(sourceIp.toInt());
222 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
223 ip4Packet.setPayload(igmpPacket);
224 ip4Packet.setParent(ethPkt);
225 ip4Packet.setTtl((byte) 0x78);
226
227 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
228 ethPkt.setSourceMACAddress(MAC_ADDRESS);
229 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
230 ethPkt.setPayload(ip4Packet);
Esin Karaman586f1d62020-06-04 10:15:34 +0000231 ethPkt.setVlanID(vlan);
ke han81a38b92017-03-10 18:41:44 +0800232 ethPkt.setPriorityCode(igmpCos);
233
Esin Karaman586f1d62020-06-04 10:15:34 +0000234 if (innerVlan != VlanId.NONE.toShort()) {
235 ethPkt.setQinQTPID(Ethernet.TYPE_VLAN);
236 ethPkt.setQinQVID(vlan);
237 ethPkt.setVlanID(innerVlan);
238 ethPkt.setQinQPriorityCode(igmpCos);
239 }
240
ke han81a38b92017-03-10 18:41:44 +0800241 return ethPkt;
242 }
243
244 private MacAddress multiaddToMac(int multiaddress) {
245 byte[] b = new byte[3];
246 b[0] = (byte) (multiaddress & 0xff);
247 b[1] = (byte) (multiaddress >> 8 & 0xff);
248 b[2] = (byte) (multiaddress >> 16 & 0x7f);
249 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
250
251 MacAddress mac = MacAddress.valueOf(macByte);
252 return mac;
253 }
254
Esin Karaman00e16b72020-02-21 10:32:39 +0000255 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000256 if (!igmpLeadershipService.isLocalLeader(deviceId)) {
ke han81a38b92017-03-10 18:41:44 +0800257 return;
258 }
259
ke han81a38b92017-03-10 18:41:44 +0800260 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000261 if (IgmpManager.connectPoint == null) {
262 log.warn("cannot find a connectPoint to send the packet uplink");
263 return;
264 }
ke han81a38b92017-03-10 18:41:44 +0800265 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
266 } else {
Esin Karaman00e16b72020-02-21 10:32:39 +0000267 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800268 }
269 }
270
271 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000272 if (!igmpLeadershipService.isLocalLeader(deviceId)) {
ke han81a38b92017-03-10 18:41:44 +0800273 return;
274 }
275
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000276 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
277 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
278 // We are checking the length of packets. Right now the counter value will be 0 because of internal translation
279 // As packet length will always be valid
280 // This counter will be useful in future if we change the procedure to generate the packets.
281 if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
282 || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.serialize().length < IGMPv2.HEADER_LENGTH) {
283 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
284 } else if (igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
285 && igmp.serialize().length < IGMPv3.MINIMUM_HEADER_LEN) {
286 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
287 }
ke han81a38b92017-03-10 18:41:44 +0800288 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
289 .setOutput(portNumber).build();
290 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
291 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000292 igmpStatisticsService.getIgmpStats().increaseValidIgmpPacketCounter();
ke han81a38b92017-03-10 18:41:44 +0800293 packetService.emit(packet);
294
295 }
296}