blob: adf0e9e65e81bbce74d72d8534c43d274d9efd11 [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;
ke han81a38b92017-03-10 18:41:44 +080027import org.onosproject.net.DeviceId;
28import org.onosproject.net.PortNumber;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
30import org.onosproject.net.flow.TrafficTreatment;
31import org.onosproject.net.packet.DefaultOutboundPacket;
32import org.onosproject.net.packet.OutboundPacket;
33import org.onosproject.net.packet.PacketService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000034import org.opencord.igmpproxy.IgmpLeadershipService;
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;
60 private byte igmpCos = DEFAULT_COS;
61 private int maxResp = DEFAULT_MEX_RESP;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000062 private Logger log = LoggerFactory.getLogger(getClass());
ke han81a38b92017-03-10 18:41:44 +080063
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000064 private IgmpSender(PacketService packetService, IgmpLeadershipService igmpLeadershipService,
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000065 IgmpStatisticsService igmpStatisticsService) {
ke han81a38b92017-03-10 18:41:44 +080066 this.packetService = packetService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000067 this.igmpLeadershipService = igmpLeadershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000068 this.igmpStatisticsService = igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080069 }
70
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000071 public static void init(PacketService packetService, IgmpLeadershipService igmpLeadershipService,
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000072 IgmpStatisticsService igmpStatisticsService) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000073 instance = new IgmpSender(packetService, igmpLeadershipService, igmpStatisticsService);
ke han81a38b92017-03-10 18:41:44 +080074 }
75
76 public static IgmpSender getInstance() {
77 return instance;
78 }
79
80 public void setWithRAUplink(boolean withRaUplink) {
81 this.withRAUplink = withRaUplink;
82 }
83
84 public void setWithRADownlink(boolean withRADownlink) {
85 this.withRADownlink = withRADownlink;
86 }
87
88 public void setMvlan(short mvlan) {
89 this.mvlan = mvlan;
90 }
91
92 public void setIgmpCos(byte igmpCos) {
93 this.igmpCos = igmpCos;
94 }
95
96 public void setMaxResp(int maxResp) {
97 this.maxResp = maxResp;
98 }
99
100 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
101 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
102 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
103
104 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
105 }
106
Arjun E Kb0018fd2020-04-07 13:26:40 +0000107 public Ethernet buildIgmpV2Join(Ip4Address groupIp, Ip4Address sourceIp) {
108 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
109 return buildIgmpPacket(IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, true);
110 }
111
112 public Ethernet buildIgmpV2ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
113 return buildIgmpV2Join(groupIp, sourceIp);
114 }
115
ke han81a38b92017-03-10 18:41:44 +0800116 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
117 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
118 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
119
120 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
121 }
122
123 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
124 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
125 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
126
127 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
128 }
129
Arjun E Kb0018fd2020-04-07 13:26:40 +0000130 public Ethernet buildIgmpV2Leave(Ip4Address groupIp, Ip4Address sourceIp) {
131 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
132 return buildIgmpPacket(IGMP.TYPE_IGMPV2_LEAVE_GROUP, groupIp, igmpMembership, sourceIp, true);
133 }
134
ke han81a38b92017-03-10 18:41:44 +0800135 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp) {
136 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, true);
137 }
138
139 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp) {
140 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, false);
141 }
142
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000143 protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
ke han81a38b92017-03-10 18:41:44 +0800144 Ip4Address sourceIp, boolean isV2Query) {
145
146 IGMP igmpPacket;
147 if (isV2Query) {
148 igmpPacket = new IGMP.IGMPv2();
149 } else {
150 igmpPacket = new IGMP.IGMPv3();
151 }
152
153 IPv4 ip4Packet = new IPv4();
154 Ethernet ethPkt = new Ethernet();
Esin Karaman45d2d6a2020-05-04 12:27:55 +0000155 ethPkt.setPad(true);
ke han81a38b92017-03-10 18:41:44 +0800156
157 igmpPacket.setIgmpType(type);
158
159 switch (type) {
160 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
161 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
162 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
163
164 igmpPacket.addGroup(igmpQuery);
165 ip4Packet.setDestinationAddress(groupIp.toInt());
166 if (withRADownlink) {
167 ip4Packet.setOptions(RA_BYTES);
168 }
169 break;
170
171 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
172 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000173 log.debug("Igmp membership is not found. igmp-type {} ", type);
ke han81a38b92017-03-10 18:41:44 +0800174 return null;
175 }
176 igmpPacket.addGroup(igmpMembership);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000177 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
178
ke han81a38b92017-03-10 18:41:44 +0800179 if (withRAUplink) {
180 ip4Packet.setOptions(RA_BYTES);
181 }
182 break;
183
184 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
185 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Arjun E Kb0018fd2020-04-07 13:26:40 +0000186 if (igmpMembership == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000187 log.debug("Igmp membership is not found. igmp-type {} ", type);
Arjun E Kb0018fd2020-04-07 13:26:40 +0000188 return null;
189 }
190 igmpPacket.addGroup(igmpMembership);
191 int dst = (type == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
192 groupIp.toInt() :
193 Ip4Address.valueOf(V2_LEAVE_DST).toInt());
194 ip4Packet.setDestinationAddress(dst);
195 break;
ke han81a38b92017-03-10 18:41:44 +0800196 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000197 log.debug("Unknown igmp type: {} ", type);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000198 igmpStatisticsService.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
ke han81a38b92017-03-10 18:41:44 +0800199 return null;
200 }
201
202 igmpPacket.setParent(ip4Packet);
203 ip4Packet.setSourceAddress(sourceIp.toInt());
204 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
205 ip4Packet.setPayload(igmpPacket);
206 ip4Packet.setParent(ethPkt);
207 ip4Packet.setTtl((byte) 0x78);
208
209 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
210 ethPkt.setSourceMACAddress(MAC_ADDRESS);
211 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
212 ethPkt.setPayload(ip4Packet);
213 ethPkt.setVlanID(mvlan);
214 ethPkt.setPriorityCode(igmpCos);
215
216 return ethPkt;
217 }
218
219 private MacAddress multiaddToMac(int multiaddress) {
220 byte[] b = new byte[3];
221 b[0] = (byte) (multiaddress & 0xff);
222 b[1] = (byte) (multiaddress >> 8 & 0xff);
223 b[2] = (byte) (multiaddress >> 16 & 0x7f);
224 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
225
226 MacAddress mac = MacAddress.valueOf(macByte);
227 return mac;
228 }
229
Esin Karaman00e16b72020-02-21 10:32:39 +0000230 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000231 if (!igmpLeadershipService.isLocalLeader(deviceId)) {
ke han81a38b92017-03-10 18:41:44 +0800232 return;
233 }
234
ke han81a38b92017-03-10 18:41:44 +0800235 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000236 if (IgmpManager.connectPoint == null) {
237 log.warn("cannot find a connectPoint to send the packet uplink");
238 return;
239 }
ke han81a38b92017-03-10 18:41:44 +0800240 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
241 } else {
Esin Karaman00e16b72020-02-21 10:32:39 +0000242 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800243 }
244 }
245
246 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000247 if (!igmpLeadershipService.isLocalLeader(deviceId)) {
ke han81a38b92017-03-10 18:41:44 +0800248 return;
249 }
250
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000251 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
252 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
253 // We are checking the length of packets. Right now the counter value will be 0 because of internal translation
254 // As packet length will always be valid
255 // This counter will be useful in future if we change the procedure to generate the packets.
256 if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
257 || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.serialize().length < IGMPv2.HEADER_LENGTH) {
258 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
259 } else if (igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
260 && igmp.serialize().length < IGMPv3.MINIMUM_HEADER_LEN) {
261 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
262 }
ke han81a38b92017-03-10 18:41:44 +0800263 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
264 .setOutput(portNumber).build();
265 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
266 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000267 igmpStatisticsService.getIgmpStats().increaseValidIgmpPacketCounter();
ke han81a38b92017-03-10 18:41:44 +0800268 packetService.emit(packet);
269
270 }
271}