blob: a4cbaa5572936dedf7bd0ffe100044874da3a031 [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;
27import org.onosproject.mastership.MastershipService;
28import 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;
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";
46 static final String MAC_ADDRESS = "DE:AD:BE:EF:BA:11";
47 static final short DEFAULT_MVLAN = 4000;
48 static final byte DEFAULT_COS = 7;
49 static final int DEFAULT_MEX_RESP = 10;
50 static final byte[] RA_BYTES = {(byte) 0x94, (byte) 0x04, (byte) 0x00, (byte) 0x00};
51
52 private static IgmpSender instance = null;
53 private PacketService packetService;
54 private MastershipService mastershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000055 private IgmpStatisticsService igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080056 private boolean withRAUplink = true;
57 private boolean withRADownlink = false;
58 private short mvlan = DEFAULT_MVLAN;
59 private byte igmpCos = DEFAULT_COS;
60 private int maxResp = DEFAULT_MEX_RESP;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000061 private Logger log = LoggerFactory.getLogger(getClass());
ke han81a38b92017-03-10 18:41:44 +080062
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000063 private IgmpSender(PacketService packetService, MastershipService mastershipService,
64 IgmpStatisticsService igmpStatisticsService) {
ke han81a38b92017-03-10 18:41:44 +080065 this.packetService = packetService;
66 this.mastershipService = mastershipService;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000067 this.igmpStatisticsService = igmpStatisticsService;
ke han81a38b92017-03-10 18:41:44 +080068 }
69
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000070 public static void init(PacketService packetService, MastershipService mastershipService,
71 IgmpStatisticsService igmpStatisticsService) {
72 instance = new IgmpSender(packetService, mastershipService, 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
91 public void setIgmpCos(byte igmpCos) {
92 this.igmpCos = igmpCos;
93 }
94
95 public void setMaxResp(int maxResp) {
96 this.maxResp = maxResp;
97 }
98
99 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
100 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
101 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
102
103 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
104 }
105
106 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
107 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
108 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
109
110 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
111 }
112
113 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
114 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
115 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
116
117 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
118 }
119
120 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp) {
121 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, true);
122 }
123
124 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp) {
125 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, false);
126 }
127
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000128 protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
ke han81a38b92017-03-10 18:41:44 +0800129 Ip4Address sourceIp, boolean isV2Query) {
130
131 IGMP igmpPacket;
132 if (isV2Query) {
133 igmpPacket = new IGMP.IGMPv2();
134 } else {
135 igmpPacket = new IGMP.IGMPv3();
136 }
137
138 IPv4 ip4Packet = new IPv4();
139 Ethernet ethPkt = new Ethernet();
140
141 igmpPacket.setIgmpType(type);
142
143 switch (type) {
144 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
145 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
146 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
147
148 igmpPacket.addGroup(igmpQuery);
149 ip4Packet.setDestinationAddress(groupIp.toInt());
150 if (withRADownlink) {
151 ip4Packet.setOptions(RA_BYTES);
152 }
153 break;
154
155 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
156 if (igmpMembership == null) {
157 return null;
158 }
159 igmpPacket.addGroup(igmpMembership);
160 if (type == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT) {
161 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
162 } else {
163 ip4Packet.setDestinationAddress(groupIp.toInt());
164 }
165 if (withRAUplink) {
166 ip4Packet.setOptions(RA_BYTES);
167 }
168 break;
169
170 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
171 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
172 return null;
173 default:
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000174 igmpStatisticsService.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
ke han81a38b92017-03-10 18:41:44 +0800175 return null;
176 }
177
178 igmpPacket.setParent(ip4Packet);
179 ip4Packet.setSourceAddress(sourceIp.toInt());
180 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
181 ip4Packet.setPayload(igmpPacket);
182 ip4Packet.setParent(ethPkt);
183 ip4Packet.setTtl((byte) 0x78);
184
185 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
186 ethPkt.setSourceMACAddress(MAC_ADDRESS);
187 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
188 ethPkt.setPayload(ip4Packet);
189 ethPkt.setVlanID(mvlan);
190 ethPkt.setPriorityCode(igmpCos);
191
192 return ethPkt;
193 }
194
195 private MacAddress multiaddToMac(int multiaddress) {
196 byte[] b = new byte[3];
197 b[0] = (byte) (multiaddress & 0xff);
198 b[1] = (byte) (multiaddress >> 8 & 0xff);
199 b[2] = (byte) (multiaddress >> 16 & 0x7f);
200 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
201
202 MacAddress mac = MacAddress.valueOf(macByte);
203 return mac;
204 }
205
Esin Karaman00e16b72020-02-21 10:32:39 +0000206 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
ke han81a38b92017-03-10 18:41:44 +0800207 if (!mastershipService.isLocalMaster(deviceId)) {
208 return;
209 }
210
ke han81a38b92017-03-10 18:41:44 +0800211 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000212 if (IgmpManager.connectPoint == null) {
213 log.warn("cannot find a connectPoint to send the packet uplink");
214 return;
215 }
ke han81a38b92017-03-10 18:41:44 +0800216 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
217 } else {
Esin Karaman00e16b72020-02-21 10:32:39 +0000218 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800219 }
220 }
221
222 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
223 if (!mastershipService.isLocalMaster(deviceId)) {
224 return;
225 }
226
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000227 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
228 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
229 // We are checking the length of packets. Right now the counter value will be 0 because of internal translation
230 // As packet length will always be valid
231 // This counter will be useful in future if we change the procedure to generate the packets.
232 if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
233 || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.serialize().length < IGMPv2.HEADER_LENGTH) {
234 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
235 } else if (igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
236 && igmp.serialize().length < IGMPv3.MINIMUM_HEADER_LEN) {
237 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
238 }
ke han81a38b92017-03-10 18:41:44 +0800239 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
240 .setOutput(portNumber).build();
241 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
242 treatment, ByteBuffer.wrap(ethPkt.serialize()));
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000243 igmpStatisticsService.getIgmpStats().increaseValidIgmpPacketCounter();
ke han81a38b92017-03-10 18:41:44 +0800244 packetService.emit(packet);
245
246 }
247}