igmp proxy app
Change-Id: If053dc39b611a6176b1d59004a480d65bfac9091
diff --git a/src/main/java/org/opencord/igmpproxy/IgmpSender.java b/src/main/java/org/opencord/igmpproxy/IgmpSender.java
new file mode 100644
index 0000000..21db478
--- /dev/null
+++ b/src/main/java/org/opencord/igmpproxy/IgmpSender.java
@@ -0,0 +1,206 @@
+package org.opencord.igmpproxy;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IGMP;
+import org.onlab.packet.IGMPMembership;
+import org.onlab.packet.IGMPQuery;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketService;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Message encode and send interface for igmpproxy.
+ */
+public final class IgmpSender {
+ static final String V3_REPORT_ADDRESS = "224.0.0.22";
+ static final String MAC_ADDRESS = "DE:AD:BE:EF:BA:11";
+ static final short DEFAULT_MVLAN = 4000;
+ static final byte DEFAULT_COS = 7;
+ static final int DEFAULT_MEX_RESP = 10;
+ static final byte[] RA_BYTES = {(byte) 0x94, (byte) 0x04, (byte) 0x00, (byte) 0x00};
+
+ private static IgmpSender instance = null;
+ private PacketService packetService;
+ private MastershipService mastershipService;
+ private boolean withRAUplink = true;
+ private boolean withRADownlink = false;
+ private short mvlan = DEFAULT_MVLAN;
+ private byte igmpCos = DEFAULT_COS;
+ private int maxResp = DEFAULT_MEX_RESP;
+
+ private IgmpSender(PacketService packetService, MastershipService mastershipService) {
+ this.packetService = packetService;
+ this.mastershipService = mastershipService;
+ }
+
+ public static void init(PacketService packetService, MastershipService mastershipService) {
+ instance = new IgmpSender(packetService, mastershipService);
+ }
+
+ public static IgmpSender getInstance() {
+ return instance;
+ }
+
+ public void setWithRAUplink(boolean withRaUplink) {
+ this.withRAUplink = withRaUplink;
+ }
+
+ public void setWithRADownlink(boolean withRADownlink) {
+ this.withRADownlink = withRADownlink;
+ }
+
+ public void setMvlan(short mvlan) {
+ this.mvlan = mvlan;
+ }
+
+ public void setIgmpCos(byte igmpCos) {
+ this.igmpCos = igmpCos;
+ }
+
+ public void setMaxResp(int maxResp) {
+ this.maxResp = maxResp;
+ }
+
+ public Ethernet buildIgmpV3Join(Ip4Address groupIp, Ip4Address sourceIp) {
+ IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+ igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_EXCLUDE_MODE);
+
+ return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
+ }
+
+ public Ethernet buildIgmpV3ResponseQuery(Ip4Address groupIp, Ip4Address sourceIp) {
+ IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+ igmpMembership.setRecordType(IGMPMembership.MODE_IS_EXCLUDE);
+
+ return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
+ }
+
+ public Ethernet buildIgmpV3Leave(Ip4Address groupIp, Ip4Address sourceIp) {
+ IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+ igmpMembership.setRecordType(IGMPMembership.CHANGE_TO_INCLUDE_MODE);
+
+ return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp, igmpMembership, sourceIp, false);
+ }
+
+ public Ethernet buildIgmpV2Query(Ip4Address groupIp, Ip4Address sourceIp) {
+ return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, true);
+ }
+
+ public Ethernet buildIgmpV3Query(Ip4Address groupIp, Ip4Address sourceIp) {
+ return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, false);
+ }
+
+ private Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
+ Ip4Address sourceIp, boolean isV2Query) {
+
+ IGMP igmpPacket;
+ if (isV2Query) {
+ igmpPacket = new IGMP.IGMPv2();
+ } else {
+ igmpPacket = new IGMP.IGMPv3();
+ }
+
+ IPv4 ip4Packet = new IPv4();
+ Ethernet ethPkt = new Ethernet();
+
+ igmpPacket.setIgmpType(type);
+
+ switch (type) {
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
+ igmpPacket.setMaxRespCode((byte) (maxResp * 10));
+ IGMPQuery igmpQuery = new IGMPQuery(groupIp, 0);
+
+ igmpPacket.addGroup(igmpQuery);
+ ip4Packet.setDestinationAddress(groupIp.toInt());
+ if (withRADownlink) {
+ ip4Packet.setOptions(RA_BYTES);
+ }
+ break;
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ if (igmpMembership == null) {
+ return null;
+ }
+ igmpPacket.addGroup(igmpMembership);
+ if (type == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT) {
+ ip4Packet.setDestinationAddress(Ip4Address.valueOf(V3_REPORT_ADDRESS).toInt());
+ } else {
+ ip4Packet.setDestinationAddress(groupIp.toInt());
+ }
+ if (withRAUplink) {
+ ip4Packet.setOptions(RA_BYTES);
+ }
+ break;
+
+ case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
+ case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
+ return null;
+ default:
+ return null;
+ }
+
+ igmpPacket.setParent(ip4Packet);
+ ip4Packet.setSourceAddress(sourceIp.toInt());
+ ip4Packet.setProtocol(IPv4.PROTOCOL_IGMP);
+ ip4Packet.setPayload(igmpPacket);
+ ip4Packet.setParent(ethPkt);
+ ip4Packet.setTtl((byte) 0x78);
+
+ ethPkt.setDestinationMACAddress(multiaddToMac(ip4Packet.getDestinationAddress()));
+ ethPkt.setSourceMACAddress(MAC_ADDRESS);
+ ethPkt.setEtherType(Ethernet.TYPE_IPV4);
+ ethPkt.setPayload(ip4Packet);
+ ethPkt.setVlanID(mvlan);
+ ethPkt.setPriorityCode(igmpCos);
+
+ return ethPkt;
+ }
+
+ private MacAddress multiaddToMac(int multiaddress) {
+ byte[] b = new byte[3];
+ b[0] = (byte) (multiaddress & 0xff);
+ b[1] = (byte) (multiaddress >> 8 & 0xff);
+ b[2] = (byte) (multiaddress >> 16 & 0x7f);
+ byte[] macByte = {0x01, 0x00, 0x5e, b[2], b[1], b[0]};
+
+ MacAddress mac = MacAddress.valueOf(macByte);
+ return mac;
+ }
+
+ public void sendIgmpPacketUplink(Ethernet ethPkt, DeviceId deviceId) {
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
+ }
+
+
+ if (IgmpManager.connectPointMode) {
+ sendIgmpPacket(ethPkt, IgmpManager.connectPoint.deviceId(), IgmpManager.connectPoint.port());
+ } else {
+ PortNumber upLink = IgmpManager.getDeviceUplink(deviceId);
+ sendIgmpPacket(ethPkt, deviceId, upLink);
+ }
+ }
+
+ public void sendIgmpPacket(Ethernet ethPkt, DeviceId deviceId, PortNumber portNumber) {
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
+ }
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(portNumber).build();
+ OutboundPacket packet = new DefaultOutboundPacket(deviceId,
+ treatment, ByteBuffer.wrap(ethPkt.serialize()));
+ packetService.emit(packet);
+
+ }
+}