/*_########################################################################## _## _## Copyright (C) 2012-2013 Kaito Yamada _## _########################################################################## */ package com.github.kaitoy.sneo.network; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.pcap4j.core.PacketListener; import org.pcap4j.packet.Packet; import org.pcap4j.util.MacAddress; import org.snmp4j.log.LogAdapter; import org.snmp4j.log.LogFactory; import com.github.kaitoy.sneo.network.protocol.Dot1qVlanTagHelper; import com.github.kaitoy.sneo.network.protocol.EthernetHelper; public class VlanInterface implements NetworkInterface { private static final LogAdapter logger = LogFactory.getLogger(VlanInterface.class); private final String name; private final MacAddress macAddress; private final List<NifIpAddress> ipAddresses = Collections.synchronizedList(new ArrayList<NifIpAddress>()); private final int vid; private final Map<String, NetworkInterface> memberNifs = new ConcurrentHashMap<String, NetworkInterface>(); private final PacketListener host; private final List<PacketListener> users = Collections.synchronizedList(new ArrayList<PacketListener>()); private volatile boolean running = false; public VlanInterface( String name, MacAddress macAddress, int vid, PacketListener host ) { this.name = name; this.macAddress = macAddress; this.vid = vid; this.host = host; } public String getName() { return name; } public MacAddress getMacAddress() { return macAddress; } public boolean isTrunk() { return false; } public List<NifIpAddress> getIpAddresses() { return new ArrayList<NifIpAddress>(ipAddresses); } public void addIpAddress(NifIpAddress addr) { ipAddresses.add(addr); } public void addUser(PacketListener user) { users.add(user); } public int getVid() { return vid; } public void start() { running = true; } public void stop() { running = false; } public void shutdown() { stop(); } public boolean isRunning() { return running; } public void addNif(String ifName, NetworkInterface nif) { if (memberNifs.containsKey(ifName)) { return; } nif.addUser(new PacketListenerImpl(ifName)); memberNifs.put(ifName, nif); } public void sendPacket(Packet packet) throws SendPacketException { if (!running) { if (logger.isDebugEnabled()) { logger.warn("Not running. Can't send a packet: " + packet); throw new SendPacketException(); } } Packet taggedPacket = null; Packet untaggedPacket = null; for (NetworkInterface nif: memberNifs.values()) { Packet sendingPacket; if (nif.isTrunk()) { if (taggedPacket == null) { taggedPacket = Dot1qVlanTagHelper.tag(packet, vid); } sendingPacket = taggedPacket; } else { if (untaggedPacket == null) { untaggedPacket = Dot1qVlanTagHelper.untag(packet); } sendingPacket = untaggedPacket; } try { nif.sendPacket(sendingPacket); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Sent a packet via ") .append(nif.getName()) .append(": ") .append(sendingPacket); logger.debug(sb.toString()); } } catch (SendPacketException e) { logger.error("Failed to send a packet: " + sendingPacket, e); } } } private final class PacketListenerImpl implements PacketListener { private final String ifName; private PacketListenerImpl(String ifName) { this.ifName = ifName; } public void gotPacket(Packet packet) { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Received a packet from ") .append(ifName) .append(": ") .append(packet); logger.debug(sb.toString()); } for (PacketListener user: users) { user.gotPacket(packet); } if (memberNifs.get(ifName).isTrunk()) { if (!Dot1qVlanTagHelper.isTagged(packet, vid)) { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Dropped a packet not tagged with VLAN ") .append(vid) .append(": ") .append(packet); logger.debug(sb.toString()); } return; } } // forwarding Packet taggedPacket = null; Packet untaggedPacket = null; for (NetworkInterface nif: memberNifs.values()) { if (ifName.equals(nif.getName())) { continue; } Packet sendingPacket; if (nif.isTrunk()) { if (taggedPacket == null) { taggedPacket = Dot1qVlanTagHelper.tag(packet, vid); } sendingPacket = taggedPacket; } else { if (untaggedPacket == null) { untaggedPacket = Dot1qVlanTagHelper.untag(packet); } sendingPacket = untaggedPacket; } try { nif.sendPacket(sendingPacket); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Forwarded a packet from ") .append(ifName) .append(" to ") .append(nif.getName()) .append(": ") .append(sendingPacket); logger.debug(sb.toString()); } } catch (SendPacketException e) { logger.error("Failed to forward a packet: " + sendingPacket, e); } } if (EthernetHelper.matchesDestination(packet, macAddress)) { host.gotPacket(packet); } else { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Dropped a packet not to me(") .append(macAddress) .append("): ") .append(packet); logger.debug(sb.toString()); } } } } }