blob: b455cb47dfa50782b76677e4a1e98eebe5c285dc [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 */
ke han81a38b92017-03-10 18:41:44 +080016package org.opencord.igmpproxy;
17
18import org.onlab.packet.Ethernet;
19import org.onlab.packet.IGMP;
20import org.onlab.packet.IGMPMembership;
21import org.onlab.packet.IGMPQuery;
22import org.onlab.packet.IPv4;
23import org.onlab.packet.Ip4Address;
24import org.onlab.packet.MacAddress;
25import org.onosproject.mastership.MastershipService;
26import org.onosproject.net.DeviceId;
27import org.onosproject.net.PortNumber;
28import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.packet.DefaultOutboundPacket;
31import org.onosproject.net.packet.OutboundPacket;
32import org.onosproject.net.packet.PacketService;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
ke han81a38b92017-03-10 18:41:44 +080035
36import java.nio.ByteBuffer;
37
38/**
39 * Message encode and send interface for igmpproxy.
40 */
41public final class IgmpSender {
42 static final String V3_REPORT_ADDRESS = "224.0.0.22";
43 static final String MAC_ADDRESS = "DE:AD:BE:EF:BA:11";
44 static final short DEFAULT_MVLAN = 4000;
45 static final byte DEFAULT_COS = 7;
46 static final int DEFAULT_MEX_RESP = 10;
47 static final byte[] RA_BYTES = {(byte) 0x94, (byte) 0x04, (byte) 0x00, (byte) 0x00};
48
49 private static IgmpSender instance = null;
50 private PacketService packetService;
51 private MastershipService mastershipService;
52 private boolean withRAUplink = true;
53 private boolean withRADownlink = false;
54 private short mvlan = DEFAULT_MVLAN;
55 private byte igmpCos = DEFAULT_COS;
56 private int maxResp = DEFAULT_MEX_RESP;
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +000057 private Logger log = LoggerFactory.getLogger(getClass());
ke han81a38b92017-03-10 18:41:44 +080058
59 private IgmpSender(PacketService packetService, MastershipService mastershipService) {
60 this.packetService = packetService;
61 this.mastershipService = mastershipService;
62 }
63
64 public static void init(PacketService packetService, MastershipService mastershipService) {
65 instance = new IgmpSender(packetService, mastershipService);
66 }
67
68 public static IgmpSender getInstance() {
69 return instance;
70 }
71
72 public void setWithRAUplink(boolean withRaUplink) {
73 this.withRAUplink = withRaUplink;
74 }
75
76 public void setWithRADownlink(boolean withRADownlink) {
77 this.withRADownlink = withRADownlink;
78 }
79
80 public void setMvlan(short mvlan) {
81 this.mvlan = mvlan;
82 }
83
84 public void setIgmpCos(byte igmpCos) {
85 this.igmpCos = igmpCos;
86 }
87
88 public void setMaxResp(int maxResp) {
89 this.maxResp = maxResp;
90 }
91
92 public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
93 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
94 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
95
96 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
97 }
98
99 public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
100 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
101 igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
102
103 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
104 }
105
106 public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
107 IGMPMembership igmpMembership = new IGMPMembership(groupIp);
108 igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
109
110 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
111 }
112
113 public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp) {
114 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, true);
115 }
116
117 public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp) {
118 return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, false);
119 }
120
121 private Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
122 Ip4Address sourceIp, boolean isV2Query) {
123
124 IGMP igmpPacket;
125 if (isV2Query) {
126 igmpPacket = new IGMP.IGMPv2();
127 } else {
128 igmpPacket = new IGMP.IGMPv3();
129 }
130
131 IPv4 ip4Packet = new IPv4();
132 Ethernet ethPkt = new Ethernet();
133
134 igmpPacket.setIgmpType(type);
135
136 switch (type) {
137 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
138 igmpPacket.setMaxRespCode((byte) (maxResp * 10));
139 IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
140
141 igmpPacket.addGroup(igmpQuery);
142 ip4Packet.setDestinationAddress(groupIp.toInt());
143 if (withRADownlink) {
144 ip4Packet.setOptions(RA_BYTES);
145 }
146 break;
147
148 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
149 if (igmpMembership == null) {
150 return null;
151 }
152 igmpPacket.addGroup(igmpMembership);
153 if (type == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT) {
154 ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
155 } else {
156 ip4Packet.setDestinationAddress(groupIp.toInt());
157 }
158 if (withRAUplink) {
159 ip4Packet.setOptions(RA_BYTES);
160 }
161 break;
162
163 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
164 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
165 return null;
166 default:
167 return null;
168 }
169
170 igmpPacket.setParent(ip4Packet);
171 ip4Packet.setSourceAddress(sourceIp.toInt());
172 ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
173 ip4Packet.setPayload(igmpPacket);
174 ip4Packet.setParent(ethPkt);
175 ip4Packet.setTtl((byte) 0x78);
176
177 ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
178 ethPkt.setSourceMACAddress(MAC_ADDRESS);
179 ethPkt.setEtherType(Ethernet.TYPE_IPV4);
180 ethPkt.setPayload(ip4Packet);
181 ethPkt.setVlanID(mvlan);
182 ethPkt.setPriorityCode(igmpCos);
183
184 return ethPkt;
185 }
186
187 private MacAddress multiaddToMac(int multiaddress) {
188 byte[] b = new byte[3];
189 b[0] = (byte) (multiaddress & 0xff);
190 b[1] = (byte) (multiaddress >> 8 & 0xff);
191 b[2] = (byte) (multiaddress >> 16 & 0x7f);
192 byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
193
194 MacAddress mac = MacAddress.valueOf(macByte);
195 return mac;
196 }
197
Esin Karaman305908c2020-02-24 14:42:52 +0000198 public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId, PortNumber upLinkPort) {
ke han81a38b92017-03-10 18:41:44 +0800199 if (!mastershipService.isLocalMaster(deviceId)) {
200 return;
201 }
202
ke han81a38b92017-03-10 18:41:44 +0800203 if (IgmpManager.connectPointMode) {
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000204 if (IgmpManager.connectPoint == null) {
205 log.warn("cannot find a connectPoint to send the packet uplink");
206 return;
207 }
ke han81a38b92017-03-10 18:41:44 +0800208 sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
209 } else {
Esin Karaman305908c2020-02-24 14:42:52 +0000210 sendIgmpPacket(ethPkt, deviceId, upLinkPort);
ke han81a38b92017-03-10 18:41:44 +0800211 }
212 }
213
214 public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
215 if (!mastershipService.isLocalMaster(deviceId)) {
216 return;
217 }
218
219 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
220 .setOutput(portNumber).build();
221 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
222 treatment, ByteBuffer.wrap(ethPkt.serialize()));
223 packetService.emit(packet);
224
225 }
226}