From 01333c235c169e4286cb0bb962d09562a7c262a4 Mon Sep 17 00:00:00 2001 From: Enrico Joerns Date: Fri, 21 Feb 2014 03:06:14 +0100 Subject: [PATCH] [cooja] interfaces/IPAddress: Reimplementation of the IPAddress interface New features: * extended interface * Shows all IPv6 addresses of a mote * Shows link local IP in visualizer * Reads IPs correctly from different memory layouts --- .../contikios/cooja/interfaces/IPAddress.java | 351 ++++++++++++++---- .../plugins/skins/AddressVisualizerSkin.java | 7 +- 2 files changed, 280 insertions(+), 78 deletions(-) diff --git a/tools/cooja/java/org/contikios/cooja/interfaces/IPAddress.java b/tools/cooja/java/org/contikios/cooja/interfaces/IPAddress.java index b9834aacc..2c47d3867 100644 --- a/tools/cooja/java/org/contikios/cooja/interfaces/IPAddress.java +++ b/tools/cooja/java/org/contikios/cooja/interfaces/IPAddress.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2014, TU Braunschweig. * Copyright (c) 2006, Swedish Institute of Computer Science. * All rights reserved. * @@ -26,151 +27,279 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + package org.contikios.cooja.interfaces; import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import java.util.Observable; import java.util.Observer; - import javax.swing.JLabel; import javax.swing.JPanel; - import org.apache.log4j.Logger; -import org.jdom.Element; - import org.contikios.cooja.ClassDescription; import org.contikios.cooja.Mote; import org.contikios.cooja.MoteInterface; import org.contikios.cooja.mote.memory.MemoryInterface; import org.contikios.cooja.mote.memory.MemoryInterface.SegmentMonitor; +import org.contikios.cooja.mote.memory.MemoryLayout; import org.contikios.cooja.mote.memory.VarMemory; import org.contikios.cooja.util.IPUtils; +import org.jdom.Element; /** * Read-only interface to IPv4 or IPv6 address. * * @author Fredrik Osterlind + * @author Enrico Joerns */ -@ClassDescription("IP Address") +@ClassDescription("IP Addresses") public class IPAddress extends MoteInterface { - private static Logger logger = Logger.getLogger(IPAddress.class); - private final VarMemory moteMem; - + private static final Logger logger = Logger.getLogger(IPAddress.class); private static final int IPv6_MAX_ADDRESSES = 4; - private boolean ipv6IsGlobal = false; - private int ipv6AddressIndex = -1; - private static final int MONITORED_SIZE = 150; - private SegmentMonitor memMonitor; + + private enum IPv { + NONE, + IPv4, + IPv6 + } + + private final IPv ipVersion; + + private final VarMemory moteMem; + private final MemoryLayout memLayout; + private IPContainer localIPAddr = null; + + private final SegmentMonitor memMonitor; + + private List ipList = new LinkedList<>(); + + private int ipv6_addr_size = 0; + private int ipv6_addr_list_offset = 0; public IPAddress(final Mote mote) { moteMem = new VarMemory(mote.getMemory()); + memLayout = mote.getMemory().getLayout(); + /* If the ip memory sections changed, we recalculate addresses + * and notify our observers.*/ memMonitor = new MemoryInterface.SegmentMonitor() { + int accessCount = 0; + long lastAccess = 0; @Override public void memoryChanged(MemoryInterface memory, SegmentMonitor.EventType type, long address) { if (type != SegmentMonitor.EventType.WRITE) { return; } - setChanged(); - notifyObservers(); + + /* XXX Quick & Dirty IPv4 update handle */ + if (ipVersion == IPv.IPv4) { + updateIPAddresses(); + setChanged(); + notifyObservers(); + return; + } + + /* Wait until size and offsest values are set initially, + * then add memory monitor for each ip field */ + if ((ipv6_addr_list_offset == 0) || (ipv6_addr_size == 0)) { + ipv6_addr_list_offset = moteMem.getByteValueOf("uip_ds6_netif_addr_list_offset"); + ipv6_addr_size = moteMem.getByteValueOf("uip_ds6_addr_size"); + /* If the variables just updated, add the final ip listeners */ + if ((ipv6_addr_list_offset != 0) && (ipv6_addr_size != 0)) { + /* Add monitor for each IP region */ + for (int i = 0; i < IPv6_MAX_ADDRESSES; i++) { + long addr_of_ip = moteMem.getVariableAddress("uip_ds6_if") // start address of interface + + ipv6_addr_list_offset // offset to ip address region + + i * ipv6_addr_size // offset to ith ip address + + 1 + memory.getLayout().getPaddingBytesFor( + MemoryLayout.DataType.INT8, + MemoryLayout.DataType.INT16); // skip 'isused' + moteMem.addMemoryMonitor( + EventType.WRITE, + addr_of_ip, + 16, /* Size of ip address in byte */ + memMonitor); + } + /* Initial scan for IP address */ + updateIPAddresses(); + if (ipList.size() > 0) { + setChanged(); + notifyObservers(); + } + /** @TODO: Remove other listeners? */ + } + } else { + + /** Note: works when 'isused' bit is set first + * and address region is written sequentially */ + + /* check for sequential reading of 16 byte block */ + if (address == lastAccess + 1) { + accessCount++; + lastAccess = address; + if (accessCount == 16) { + updateIPAddresses(); + setChanged(); + notifyObservers(); + lastAccess = 0; + } + } + else { + /* Check if ip write was interrupted unexpectedly last time */ + if (lastAccess != 0) { + updateIPAddresses(); + setChanged(); + notifyObservers(); + } + accessCount = 1; + lastAccess = address; + } + } } }; - if (isVersion4()) { - moteMem.addVarMonitor(SegmentMonitor.EventType.WRITE, "uip_hostaddr", memMonitor); - } else if (isVersion6()) { - moteMem.addVarMonitor(SegmentMonitor.EventType.WRITE, "uip_ds6_netif_addr_list_offset", memMonitor); - moteMem.addVarMonitor(SegmentMonitor.EventType.WRITE, "uip_ds6_addr_size", memMonitor); - moteMem.addVarMonitor(SegmentMonitor.EventType.WRITE, "uip_ds6_if", memMonitor); + + /* Determine IP version an add MemoryMonitors */ + if (moteMem.variableExists("uip_hostaddr")) { + logger.debug("IPv4 detected"); + ipVersion = IPv.IPv4; + moteMem.addVarMonitor( + SegmentMonitor.EventType.WRITE, + "uip_hostaddr", + memMonitor); + } else if (moteMem.variableExists("uip_ds6_netif_addr_list_offset") + && moteMem.variableExists("uip_ds6_addr_size") + && moteMem.variableExists("uip_ds6_if")) { + logger.debug("IPv6 detected"); + ipVersion = IPv.IPv6; + moteMem.addVarMonitor( + SegmentMonitor.EventType.WRITE, + "uip_ds6_netif_addr_list_offset", + memMonitor); + moteMem.addVarMonitor( + SegmentMonitor.EventType.WRITE, + "uip_ds6_addr_size", + memMonitor); + } else { + ipVersion = IPv.NONE; } + + // initially look for IPs we already have + updateIPAddresses(); + } + + /** + * Returns true if any IP stack (Ipv4/6) is supported by mote + * @return true if either IPv4 or IPv6 was detected + */ + public boolean hasIP() { + return !(ipVersion == IPv.NONE); + } + + /** + * Get local IP of mote. + * @return local IP or null if not existing + */ + public IPContainer getLocalIP() { + return localIPAddr; } /** * Returns IP address string. * Supports both IPv4 and IPv6 addresses. * + * @param idx * @return IP address string */ - public String getIPString() { - if (isVersion4()) { - byte[] ip = moteMem.getByteArray("uip_hostaddr", 4); - return IPUtils.getIPv4AddressString(ip); - } else if (isVersion6()) { - return IPUtils.getCompressedIPv6AddressString(getIPv6Address()); + public IPContainer getIP(int idx) { + try { + return ipList.get(idx); + } catch (IndexOutOfBoundsException ex) { + logger.warn("Invalid IP index " + idx); + return null; } - return null; } - public byte[] getIPv6Address() { - byte[] ip = null; + /** + * Rereads IP addresses from memory and updates localIP entry. + */ + private void updateIPAddresses() { + ipList.clear(); + if (ipVersion == IPv.IPv4) { + addIPv4Addresses(); + localIPAddr = ipList.get(0); + } + else if (ipVersion == IPv.IPv6) { + addIPv6Addresses(); + /* look for local ip addr */ + for (IPContainer c : ipList) { + if (!c.isGlobal) { + localIPAddr = c; + } + } + } + } + + /** + * Rereads IPv4 addresses from memory. + */ + private void addIPv4Addresses() { + ipList.add(new IPContainer(0, moteMem.getByteArray("uip_hostaddr", 4), true)); + } + + /** + * Rereads IPv6 addresses from memory. + */ + private void addIPv6Addresses() { /* IpV6: Struct sizes and offsets */ int ipv6NetworkInterfaceAddressOffset = moteMem.getByteValueOf("uip_ds6_netif_addr_list_offset"); int ipv6AddressStructSize = moteMem.getByteValueOf("uip_ds6_addr_size"); + /* check if addresses were not set yet */ if (ipv6NetworkInterfaceAddressOffset == 0 || ipv6AddressStructSize == 0) { - return null; + return; } - /* TODO No need to copy the entire array! */ byte[] structData = moteMem.getByteArray( - "uip_ds6_if", - ipv6NetworkInterfaceAddressOffset + IPv6_MAX_ADDRESSES * ipv6AddressStructSize); - - ipv6AddressIndex = -1; + moteMem.getVariableAddress("uip_ds6_if") + ipv6NetworkInterfaceAddressOffset, + IPv6_MAX_ADDRESSES * ipv6AddressStructSize); + for (int addressIndex = 0; addressIndex < IPv6_MAX_ADDRESSES; addressIndex++) { - int offset = ipv6NetworkInterfaceAddressOffset + addressIndex * ipv6AddressStructSize; + int offset = addressIndex * ipv6AddressStructSize; byte isUsed = structData[offset]; if (isUsed == 0) { continue; } byte[] addressData = new byte[16]; System.arraycopy( - structData, offset + 2/* ipaddr offset */, + structData, offset + 1 + memLayout.getPaddingBytesFor( + MemoryLayout.DataType.INT8, + MemoryLayout.DataType.INT16),/* ipaddr offset */ addressData, 0, 16); - if (addressData[0] == (byte) 0xFE && addressData[1] == (byte) 0x80) { - ipv6IsGlobal = false; - } else { - ipv6IsGlobal = true; + if (((addressData[0] & (byte) 0xFF) == (byte) 0xFE) && ((addressData[1] & (byte) 0xFF) == (byte) 0x80)) { + ipList.add(new IPContainer(addressIndex, addressData, false)); + } + else { + ipList.add(new IPContainer(addressIndex, addressData, true)); } - ip = addressData; - ipv6AddressIndex = addressIndex; - if (ipv6IsGlobal) { - break; - } } - if (ip == null) { - ip = new byte[16]; - ipv6AddressIndex = -1; - } - return ip; } - /** - * @return True if mote has an IPv4 address - */ - public boolean isVersion4() { - return moteMem.variableExists("uip_hostaddr"); - } - - /** - * @return True if mote has an IPv6 address - */ - public boolean isVersion6() { - return moteMem.variableExists("uip_ds6_netif_addr_list_offset") - && moteMem.variableExists("uip_ds6_addr_size") - && moteMem.variableExists("uip_ds6_if"); - } + // -- MoteInterface overrides @Override public void removed() { super.removed(); if (memMonitor != null) { - if (isVersion4()) { - moteMem.removeVarMonitor("rimeaddr_node_addr", memMonitor); - } else if (isVersion6()) { + if (ipVersion == IPv.IPv4) { + moteMem.removeVarMonitor("uip_hostaddr",memMonitor); + } + else if (ipVersion == IPv.IPv6) { moteMem.removeVarMonitor("uip_ds6_netif_addr_list_offset", memMonitor); moteMem.removeVarMonitor("uip_ds6_addr_size", memMonitor); moteMem.removeVarMonitor("uip_ds6_if", memMonitor); @@ -187,14 +316,28 @@ public class IPAddress extends MoteInterface { this.addObserver(observer = new Observer() { @Override public void update(Observable obs, Object obj) { - if (isVersion4()) { - ipLabel.setText("IPv4 address: " + getIPString()); - } else if (isVersion6()) { - ipLabel.setText((ipv6IsGlobal ? "Global" : "Local") - + " IPv6 address(#" + ipv6AddressIndex + "): " + getIPString()); - } else { - ipLabel.setText("Unknown IP"); + StringBuilder ipStr = new StringBuilder(); + ipStr.append(""); + for (IPContainer ipc: ipList) { + if (ipVersion == IPv.IPv4) { + ipStr.append("IPv4 address: ") + .append(ipc.toString()) + .append("
"); + } + else if (ipVersion == IPv.IPv6) { + ipStr.append(ipc.isGlobal() ? "Global" : "Local") + .append(" IPv6 address(#") + .append(ipc.getAddID()) + .append("): ") + .append(ipc.toString()) + .append("
"); + } + else { + ipStr.append("Unknown IP
"); + } } + ipStr.append(""); + ipLabel.setText(ipStr.toString()); } }); observer.update(null, null); @@ -223,4 +366,60 @@ public class IPAddress extends MoteInterface { @Override public void setConfigXML(Collection configXML, boolean visAvailable) { } + + /** + * Holds a single IP address. + * + * Note: The compressed IP version is already computed in constructor + */ + public class IPContainer { + + private boolean isGlobal = false; + private final byte[] ip; + private final int addrIdx; + private final String cprString; + + public IPContainer(int addidx, byte[] ip, boolean global) { + this.addrIdx = addidx; + this.ip = ip; + this.isGlobal = global; + if (ipVersion == IPv.IPv4) { + cprString = IPUtils.getIPv4AddressString(ip); + } else if (ipVersion == IPv.IPv6) { + cprString = IPUtils.getCompressedIPv6AddressString(ip); + } else { + cprString = ""; + } + /* logger.info("Added new IP: " + cprString); */ + } + + public int getAddID() { + return addrIdx; + } + + public boolean isGlobal() { + return isGlobal; + } + + public byte[] getIP() { + return ip; + } + + @Override + public String toString() { + return cprString; + } + + public String toUncompressedString() { + if (ipVersion == IPv.IPv4) { + return cprString; + } + else if (ipVersion == IPv.IPv6) { + return IPUtils.getUncompressedIPv6AddressString(ip); + } + else { + return ""; + } + } + } } diff --git a/tools/cooja/java/org/contikios/cooja/plugins/skins/AddressVisualizerSkin.java b/tools/cooja/java/org/contikios/cooja/plugins/skins/AddressVisualizerSkin.java index e8dbefd02..44a77fc6a 100644 --- a/tools/cooja/java/org/contikios/cooja/plugins/skins/AddressVisualizerSkin.java +++ b/tools/cooja/java/org/contikios/cooja/plugins/skins/AddressVisualizerSkin.java @@ -128,8 +128,11 @@ public class AddressVisualizerSkin implements VisualizerSkin { private static String getMoteString(Mote mote) { IPAddress ipAddr = mote.getInterfaces().getIPAddress(); - if (ipAddr != null && ipAddr.getIPString() != null) { - return ipAddr.getIPString(); + if ((ipAddr != null) && (ipAddr.hasIP())) { + if (ipAddr.getLocalIP() == null) { + return ""; + } + return ipAddr.getLocalIP().toString(); } RimeAddress rimeAddr = mote.getInterfaces().getRimeAddress();