Can OMNET with INET4.4 be used for large-scale simulations? - simulation

everybody!
I want to simulate a large-scale (more than thousand nodes) satellite network,e.g.,starlink_p1 constellation with 1584 satellites.
We model satellite as router, and each router connects to a standhost for generating traffic. We use the up-to-date version of OMNET with INET4.4 in Ubuntu20.04, and the topology file (NED) and config file(ini) are complete as the following.
However, when we run the simulation by the means of Cmdenv, the simulation has been initializing for more than a day. I wonder if there's something wrong with my setting, or if omnet does not support large-scale simulation with 1584 nodes.
I look forward to your reply, thanks.
NED.file:
package inet.examples.inet.MegaCons;
import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator;
import inet.node.inet.Router;
import inet.node.inet.StandardHost;
network Starlink_p1_grid_topo
{
parameters:
int n = 1584;
types:
channel Intra_channel extends ned.DatarateChannel
{
delay = 2.2ms;
datarate = 1Gbps;
ber = 1e-7;
}
channel Ref_channel extends ned.DatarateChannel
{
datarate = 1Gbps;
ber = 1e-7;
}
submodules:
sat[n]: Router {
gates:
pppg[4];
ethg[1];
}
H1: StandardHost {
gates:
ethg[1];
}
H2: StandardHost {
gates:
ethg[1];
}
configurator: Ipv4NetworkConfigurator {
#display("p=66,93");
}
connections allowunconnected:
sat[125].ethg++ <--> Ref_channel <--> H1.ethg++;
sat[853].ethg++ <--> Ref_channel <--> H2.ethg++;
//------intra-plane ISLs----------------
for orb = 0..23,for orb_sat = 0..65 {
sat[24*orb + orb_sat].pppg++ <--> Ref_channel <--> sat[24*orb + orb_sat + 1].pppg++ if (24*orb + orb_sat < 66*(orb + 1) - 1);
sat[24*orb + orb_sat].pppg++ <--> Ref_channel <--> sat[24*orb + orb_sat - 65].pppg++ if (24*orb + orb_sat == 66*(orb + 1) - 1);
}
//------inter-plane ISLs----------------
for orb_sat = 0..65,for orb = 0..23 {
sat[orb_sat + 24*orb].pppg++ <--> Ref_channel <--> sat[orb_sat + 66*(orb + 1)].pppg++ if (orb < 23);
sat[orb_sat + 24*orb].pppg++ <--> Ref_channel <--> sat[orb_sat + 66*(orb - 23)].pppg++ if (orb == 23);
}
}
INI file :
[General]
network = Starlink_p1_grid_topo
sim-time-limit = 30s
**.module-eventlog-recording = false
**.sndNxt.statistic-recording = true
**.sndTime.statistic-recording = true
**.rcvSeq.statistic-recording = true
**.rcvTime.statistic-recording = true
**.packetSent.statistic-recording = true
**.packetReceived.statistic-recording = true
**.endToEndDelay.statistic-recording = true
**.throughput.statistic-recording = true
**.**.statistic-recording = false
**.**.bin-recording = false
[Config test]
**.H*.numApps = 2
**.app[0].typename = "UdpBasicApp"
**.app[0].destPort = 1234
**.app[0].messageLength = 128B
**.app[0].sendInterval = 0.1s
**.app[0].startTime = 10s
**.app[0].stopTime = 20s
**.H2.app[0].destAddresses = "H1"
**.H1.app[0].destAddresses = "H2"
**.app[1].typename = "UdpEchoApp"
**.app[1].localPort = 1234
And Cmdenv config is as following:

First, a few comments on your network:
if you use the ++ operator on gates, you do NOT have to specify the gate sizes (for ethg and pppg) in your hosts. The simulation kernel will allocate new gates as needed when the ++ operator is used.
It seems that you want to create a 'torus' topology in your network. Your code is sub-omptimal as it tests some conditions unnecessary in the loop and in some places it has definitely bugs (e.g. orb_sat + 24*orb seems definitely wrong to me. isn't that orb_sat + 66*orb ?).
But after correcting these issues, you will still face heavy initialization times (that are non-linear with the size of the network). The reason is the usage of the Ipv4NetworkConfigurator module. That module is very powerful, but if does NOT scale easily with the number of network, because it has to find the shortest path between ALL subnets in the network and configure optimize ALL routing tables everywhere. This is OK if you have a lot of nodes placed in a few subnets, but here you have 1584 routers with 1584 subnets. That is not something the Ipv4NetworkConfigurator can handle in a reasonable time. You MUST remove the Ipv4NetworkConfigurator module from your network and create your own configurator that set's up the routing tables correctly. I'm not sure how you intend to set up the routing at all. Probably, that's the main point of your study. I suggest to remove the network configurator and as a first step, use HostAutoConfigurator in each roiter by placing this in your INI file:
**.configurator.typename = "HostAutoConfigurator"
This just assigns an IP address to the nodes, but it does NOT set up the routing tables, so you won't be able to route packets until you properly set up all the routing tables. Probably static routing tables are not suitable at all and you would need to implement your own routing algorithm (some kind of geographic routing, where the routing is not done based on destination IP addresses, but rather on the desination's geographic location). I'm not even sure that the whole thing should be simulated at network level. Maybe it should work on link layer level? A bit modified version of your code:
network Starlink_p1_grid_topo
{
parameters:
int noOfSats = 66; // satellites per orbital plane
int noOfPlanes = 24; // number of orbital planes
types:
...
submodules:
sat[noOfSats * noOfPlanes]: Router {
#display("p=,,m,$noOfSats");
}
H1: StandardHost {
}
H2: StandardHost {
}
connections allowunconnected:
...
// connect all sat modules in a torus topology
for p = 0..noOfPlanes-1,for s = 0..noOfSats-1 {
sat[p*noOfSats + s].pppg++ <--> Ref_channel <--> sat[p*noOfSats + ((s+1) % noOfSats)].pppg++; // intra-plane link
sat[p*noOfSats + s].pppg++ <--> Ref_channel <--> sat[((p+1) % noOfPlanes)*noOfSats + s].pppg++; // inter plane-link
}
}

Related

What is the definition of type A and type B Android services?

While inspecting the Total PSS usage by OOM adjustment via dumpsys meminfo oom I came across the categories "A Services" and "B Services".
Do you know what is the exact meaning of these 2 categories?
Maybe it has something to do with the service's position in the LRU?
the following code is used by AMS to manage processes, you can read it to know more.
https://android.googlesource.com/platform/frameworks/base/+/4f868ed/services/core/java/com/android/server/am/ProcessList.java
Put it simple, About 1/3 processes in the service list are A Services, 2/3 are B Services. In some condition, some A services have big PSS will be taken as B Services.
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 8;
if (adj == ProcessList.SERVICE_ADJ) {
if (doingAll) {
app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
mNewNumServiceProcs++;
if (!app.serviceb) {
if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
&& app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
app.serviceHighRam = true;
app.serviceb = true;
} else {
mNewNumAServiceProcs++;
}
} else {
app.serviceHighRam = false;
}
}
if (app.serviceb) {
adj = ProcessList.SERVICE_B_ADJ;
}
}

How to calculate mean of distributed data?

How I can calculate the arithmetic mean of a large vector(series) in distributed computing where I partition the data on multiple nodes. I do not want to use map reduce paradigm. Is there any distributed algorithm to efficiently compute the mean besides the trivial computation of individual sum on each node and then bringing the result at master node and dividing with the size of the vector(series).
distributed average consensus is an alternative.
The problem with the trivial approach of map-reduce with a master is that if you have a vast set of data, in essence to make everything dependent on each other, it could take a very long time to calculate the data, by which time the information is very out of date, and therefore wrong, unless you lock the entire dataset - impractical for a massive set of distributed data. Using distributed average consensus (the same methods work for alternative algorithms to Mean), you get a more up to date, better guess at the current value of the Mean without locking the data, and in real time.
Here is a link to a paper on it, but it's math heavy :
http://web.stanford.edu/~boyd/papers/pdf/lms_consensus.pdf
You can google for many papers on it.
The general concept is like this: say on each node you have a socket listener. You evaluate your local sum and average, then publish it to the other nodes. Each node listens for the other nodes, and receives their sum and averages on a timescale that makes sense. You can then evaluate a good guess at the total average by (sumForAllNodes(storedAverage[node] * storedCount[node]) / (sumForAllNodes(storedCount[node])). If you have a truly large dataset, you could just listen for new values as they are stored in the node, and amend the local count and average, then publish them.
If even this is taking too long, you could average over a random subset of the data in each node.
Here is some c# code that gives you an idea (uses fleck to run on more versions of windows than windows-10-only microsoft websockets implementation). Run this on two nodes, one with
<appSettings>
<add key="thisNodeName" value="UK" />
</appSettings>
in the app.config, and use "EU-North" in the other. Here is some sample code. The two instances exchange means using websockets. You just need to add your back end enumeration of the database.
using Fleck;
namespace WebSocketServer
{
class Program
{
static List<IWebSocketConnection> _allSockets;
static Dictionary<string,decimal> _allMeans;
static Dictionary<string,decimal> _allCounts;
private static decimal _localMean;
private static decimal _localCount;
private static decimal _localAggregate_count;
private static decimal _localAggregate_average;
static void Main(string[] args)
{
_allSockets = new List<IWebSocketConnection>();
_allMeans = new Dictionary<string, decimal>();
_allCounts = new Dictionary<string, decimal>();
var serverAddresses = new Dictionary<string,string>();
//serverAddresses.Add("USA-WestCoast", "ws://127.0.0.1:58951");
//serverAddresses.Add("USA-EastCoast", "ws://127.0.0.1:58952");
serverAddresses.Add("UK", "ws://127.0.0.1:58953");
serverAddresses.Add("EU-North", "ws://127.0.0.1:58954");
//serverAddresses.Add("EU-South", "ws://127.0.0.1:58955");
foreach (var serverAddress in serverAddresses)
{
_allMeans.Add(serverAddress.Key, 0m);
_allCounts.Add(serverAddress.Key, 0m);
}
var thisNodeName = ConfigurationSettings.AppSettings["thisNodeName"]; //for example "UK"
var serverSocketAddress = serverAddresses.First(x=>x.Key==thisNodeName);
serverAddresses.Remove(thisNodeName);
var websocketServer = new Fleck.WebSocketServer(serverSocketAddress.Value);
websocketServer.Start(socket =>
{
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
_allSockets.Add(socket);
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
_allSockets.Remove(socket);
};
socket.OnMessage = message =>
{
Console.WriteLine(message + " received");
var parameters = message.Split('~');
var remoteHost = parameters[0];
var remoteMean = decimal.Parse(parameters[1]);
var remoteCount = decimal.Parse(parameters[2]);
_allMeans[remoteHost] = remoteMean;
_allCounts[remoteHost] = remoteCount;
};
});
while (true)
{
//evaluate my local average and count
Random rand = new Random(DateTime.Now.Millisecond);
_localMean = 234.00m + (rand.Next(0, 100) - 50)/10.0m;
_localCount = 222m + rand.Next(0, 100);
//evaluate my local aggregate average using means and counts sent from all other nodes
//could publish aggregate averages to other nodes, if you wanted to monitor disagreement between nodes
var total_mean_times_count = 0m;
var total_count = 0m;
foreach (var server in serverAddresses)
{
total_mean_times_count += _allCounts[server.Key]*_allMeans[server.Key];
total_count += _allCounts[server.Key];
}
//add on local mean and count which were removed from the server list earlier, so won't be processed
total_mean_times_count += (_localMean * _localCount);
total_count = total_count + _localCount;
_localAggregate_average = (total_mean_times_count/total_count);
_localAggregate_count = total_count;
Console.WriteLine("local aggregate average = {0}", _localAggregate_average);
System.Threading.Thread.Sleep(10000);
foreach (var serverAddress in serverAddresses)
{
using (var wscli = new ClientWebSocket())
{
var tokSrc = new CancellationTokenSource();
using (var task = wscli.ConnectAsync(new Uri(serverAddress.Value), tokSrc.Token))
{
task.Wait();
}
using (var task = wscli.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(thisNodeName+"~"+_localMean + "~"+_localCount)),
WebSocketMessageType.Text,
false,
tokSrc.Token
))
{
task.Wait();
}
}
}
}
}
}
}
Don't forget to add static lock or separate activity by synchronising at given times. (not shown for simplicity)
There are two simple approaches you can use.
One is, as you correctly noted, to calculate the sum on every node and then combine the sums and divide by the total amount of data:
avg = (sum1+sum2+sum3)/(cnt1+cnt2+cnt3)
Another possibility is to calculate the average on every node and then use weighted average:
avg = (avg1*cnt1 + avg2*cnt2 + avg3*cnt3) / (cnt1+cnt2+cnt3)
= avg1*cnt1/(cnt1+cnt2+cnt3) + avg2*cnt2/(cnt1+cnt2+cnt3) + avg3*cnt3/(cnt1+cnt2+cnt3)
I don't see anything wrong with these trivial ways and am wondering why you would want to use a different approach.

Database for numerical data from physics simulation

I work in theoretical physics and I do lot of computer simulations. An important part of my duty is the analysis of the results. I make simulations and store the numerical results in a file with some simple name. Typically I have lot of data files with very similar name and after a while I do not remember what kind of parameters the file corresponds to. I was thinking that maybe there exists a better way to store numerical results from a simulation e.g. some database (SQL, MongoDB etc.) where I could put some comments about parameters of the program, names, date etc. - a sort of a library with numerical data. I just have everything in a one place well organized. Do you know of anything like this? How do you store you numerical data from computer simulations?
More details
Typical procedure looks like this. Let say we want to simulate time evolution of the three body problem. We have three bodies of different masses interacting with Newton forces. I want to test how these objects move in space depending on: relative mass value, initial position - 6 parameters. I run simulation for one choice of parameters and save it in file: three_body_m1=0p1_m2=0p3_(the rest).dat - all double precision in total 1+3*3 (3d) columns of data in one file. Then I lunch gnuplot, python etc. and visualize them. In principle there is no relation between the data from different simulations, but I can use them to make comparison plot.
Within same nodejs context, you can,
Stream big xyz data file to server using socket.io-stream + fs modules and save filename+parameters to database using mongodb module.(max 1-page of coding but more for complex server talking)
If data fits in ram and if you don't have to save immediately, you can use redis module to send everything to server cache easily(as key-value pairs such as data->xyzData and parameters->simulationParameters and user->name_surname) and read from it high speed. If you need data as file by other processes in server, you can stram to a ramdisk instead and have most of RAM bandwidth as a file cache.(needs more ram ofcourse but fast)
mongodb is slow(even with optimizations) for saving millions of particles xyz data but is most easiest and quickest install for parameter saving and sharing.
Using all could be better.
Saving: stream file to physical disk using socket.io-stream and fs. Send parameters to mongodb.
Loading: check redis if user is registered, check if data is in cache, if yes, get it, if no, stream from physical disk and also save some of it to redis at the same time.
Editing: check if cache exists, if yes then edit it. Another serverside process can update physical disk from that cache, if no then update physical disk directly.
The communication scheme could be:
data server talks to cache server if there is any pending writes/reads/edits, consumes jobs from there.
compute server talks to cache server for producing read/write/edit jobs or consuming compute jobs.
clients can talk to cache server for reading only.
admins can also place their own data or produce compute jobs or read stuff.
compute server, data server and cache server can be on same computer easily or moved to other computers thanks to nodejs's awesomeness and countless modules of it such as redis, socket.io-stream, fs, ejs, express(for clients for example), etc.
a cache server can offload some data to another cache server and have a redirection to it(or some mapping of data to it)
a cache server can communicate N number of data servers and M number of compute servers at the same time as long as RAM holds.
You have slow network? You can use gzip module to compress the data on-the-fly with just 3-5 lines of extra code(at both ends)
You don't have money?
Nodejs works on raspberry pi (as data server maybe?)
Nvidia GTX660 can work with an Intel galileo (compute server?) using nodejs with some extra native modules for opencl(could be hard to implement)(also connecting(and powering) gpu and galileo may not be easy but should be much faster than a cluster of raspberry pi boards for fp32 number crunching)
bypass cache, RAM is expensive for now.
data server cluster
\
\
\ client
\ client /
\ / /
\ / /
mainframe cache and database server ----- compute cluster
| \
| \
support cache server admin
A very simple example to send some files to another computer(or same):
var pipeline_n = 8;
var fs = require("fs");
// server part accepting files
{
var io = require('socket.io').listen(80);
var ss = require('socket.io-stream');
var path = require('path');
var ctr = 0;
var ctr2 = 0;
io.of('/user').on('connection', function (socket) {
var z1 = new Date();
for (var i = 0; i < pipeline_n; i++) {
ss(socket).on('data'+i.toString(), function (stream, data) {
var t1 = new Date();
stream.pipe(fs.createWriteStream("m://bench_server" + ctr + ".txt"));
ctr++;
stream.on("finish", function (p) {
var len = stream._readableState.pipes.bytesWritten;
var t2 = new Date();
ctr2++;
if (ctr2 == pipeline_n) {
var z2 = new Date();
console.log(len * pipeline_n);
console.log((z2 - z1));
console.log("throughput: " + ((len * pipeline_n) / ((z2 - z1)/1000.0))/(1024*1024)+" MB/s");
}
});
});
}
});
}
//client or another server part sending a file
//(you can change it to do parts of same file instead of same file n times),
//just a dummy file sending code to stress other server
for (var i = 0; i < pipeline_n; i++)
{
var io = require('socket.io-client');
var ss = require('socket.io-stream');
var socket = io.connect('http://127.0.0.1/user');
var stream = ss.createStream();
var filename = 'm://bench.txt'; // ramdrive or cluster of hdd raid
ss(socket).emit('data'+i.toString(), stream, { name: filename });
fs.createReadStream(filename).pipe(stream);
}
Here is testing insert vs bulk insert performance of mongodb(this could be a wrong way to benchmark but is simple, just uncomment-in the part you want to benchmark)
var mongodb = require('mongodb');
var client = mongodb.MongoClient;
var url = 'mongodb://localhost:2019/evdb2';
client.connect(url, function (err, db) {
if (err) {
console.log('fail:', err);
} else {
console.log('success:', url);
var collection = db.collection('tablo');
var bulk = collection.initializeUnorderedBulkOp();
db.close();
//benchmark insert
//var t = 0;
//t = new Date();
//var ctr = 0;
//for (var i = 0; i < 1024 * 64; i++)
//{
// collection.insert({ x: i + 1, y: i, z: i * 10 }, function (e, r) {
// ctr++;
// if (ctr == 1024 * 64)
// {
// var t2 = 0;
// db.close();
// t2 = new Date();
// console.log("insert-64k: " + 1000.0 / ((t2.getTime() - t.getTime()) / (1024 * 64)) + " insert/s");
// }
// });
//}
// benchmark bulk insert
//var t3 = new Date();
//for (var i = 0; i < 1024 * 64; i++)
//{
// bulk.insert({ x: i + 1, y: i, z: i * 10 });
//}
//bulk.execute();
//var t4 = new Date();
//console.log("bulk-insert-64k: " + 1000.0/((t4.getTime() - t3.getTime()) / (1024 * 64)) + " insert/s");
// db.close();
}
});
be sure to setup mongodb and or redis servers before this. Also "npm install module_name" necessary modules from nodejs command prompt.

WFP ALE_CONNECT_REDIRECT layer block filter doesn't work

I am doing some work with WFP and I have the problem with blocking filter on FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 layer. It must block trafic from local ip, but it doesn't. If I change layer to FWPM_LAYER_ALE_AUTH_CONNECT_V4 filter works properly.
So I have several questions:
1) Can I block trafic from specified local ip on FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 layer (code below doesn't work)?
2) Can we create conditions with local_ip(remote_ip) on ale_connect_redirect(or ale_bind_redirect) layers?
UINT32 test_wfp_filter(HANDLE engine_handle,
FWP_V4_ADDR_AND_MASK* source_ip,
UINT8 weight)
{
UINT32 status;
FWPM_FILTER filter = { 0 };
FWPM_FILTER_CONDITION filter_conditions[1] = { 0 };
filter_conditions[0].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
filter_conditions[0].matchType = FWP_MATCH_EQUAL;
filter_conditions[0].conditionValue.type = FWP_V4_ADDR_MASK;
filter_conditions[0].conditionValue.v4AddrMask = source_ip;
status = UuidCreate(&(filter.filterKey));
if (status != NO_ERROR)
{
return status;
}
filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
//With this layerKey filter doesn't work,
//but with FWPM_LAYER_ALE_AUTH_CONNECT_V4 filter works properly
filter.displayData.name = L"Blocking filter";
filter.displayData.description = L"Blocks all trafic from current comp";
filter.action.type = FWP_ACTION_BLOCK;
filter.subLayerKey = WFP_TEST_SUBLAYER;
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = weight;
filter.filterCondition = filter_conditions;
filter.numFilterConditions = 1;
status = FwpmFilterAdd(engine_handle, &filter, 0, 0);
return 0;
}
Thank you!
It's not 100% obvious what you are trying to achieve but:
No, the ALE_CONNECT_REDIRECT and ALE_BIND_REDIRECT layers are for modifying source/destination details associated with a flow (prior to establishment), not blocking the flow. An example usage would be writing a local proxy; you might install an ALE_CONNECT_REDIRECT callout which modifies the destination details for an attempted connection such that the connection is actually made to your own application rather than where it was originally intended.
You can definitely use source and destination IP address conditions with ALE_CONNECT_REDIRECT and ALE_BIND_REDIRECT, just remember that these layers are for redirecting not blocking.

Optimal IP subnet matching

The following code appears to be the hottest spot in my program.
JAVA_OPTS=-Xprof output:
Compiled + native Method
5.7% 173 + 0 scala.collection.IndexedSeqOptimized$class.slice
5.1% 156 + 0 scala.collection.IndexedSeqOptimized$class.foreach
2.9% 87 + 0 java.util.regex.Pattern$BmpCharProperty.match
2.5% 76 + 0 scala.collection.IndexedSeqOptimized$class.sameElements
2.4% 73 + 0 trafacct.SubNet.contains
Slice, sameElements and even foreach calls seem to be most used from here too. Can someone give an advice or two on how to optimize contains() method? Maybe some techniques allowing Bytes analysis without converting them to integers? Or solid whole-sequence approach without slice?
Function SubNet.contains() matches an IP address against subnet.
object SubNet {
def toInts(bytes: Seq[Byte]): Seq[Int] = bytes.map(_.toInt & 0xFF)
}
case class SubNet(ip:InetAddress, maskLength:Int) extends HostCategory {
import SubNet.toInts
private val bytes: Int = maskLength / 8
private val subnet = toInts(ip.getAddress)
private val bits = bytes * 8 - maskLength
def contains(host: Host) = {
if (host.ip == null && ip == null) {
true
} else if (this.ip == null) {
false
} else {
val address = toInts(host.ip.getAddress)
if (address.length != subnet.length) {
false
} else {
if (address.slice(0, bytes) != subnet.slice(0, bytes)) {
false
} else {
((address(bytes) >> (8-bits) ^ subnet(bytes) >> (8-bits)) & 0xFF) == 0
}
}
}
}
}
I understand, that this optimization won't give me much better throughput, I just feel that I'm doing something wrong spending so much time inside this simple function.
This code should be IPv6 (16 bytes) compatible, and I don't like the idea of handling IPv4 case separately.
You're not doing anything wrong per se; you're just using collections that are meant for ease of use not performance when handling primitives.
If you want to speed this up, you'll get the largest boost by switching to using arrays and while loops. It's not entirely clear to me that the code you wrote even works for IPv6 except for IPv4 addresses stored in IPv6 format, since you could have a subnet with more than 256 items. Also, by testing lengths you're assuming no mixed IPv6/IPv4 representations of the same address.
I'd forget the whole "toInts" thing and just store byte arrays; then do something like (warning, untested)
def contains(host: Host): Boolean = {
//...
if (address.length != subnet.length) false
else {
var i = 0
while (i<address.length-1) {
if (address(i) != subnet(i)) return false
i += 1
}
(address(i)&0xFF) >> (8-bits) ^ (subnet(i)&0xFF) >> (8-bits) == 0
}
}
It's really not any more complicated than your original solution, and should run ~10x faster.
With this code, it does not validate correctly.
For example:
scala> val ip = java.net.InetAddress.getByName("::ffff:1.2.176.0")
ip: java.net.InetAddress = /1.2.176.0
scala> val prefix = new InetPrefix(ip, 20)
prefix: InetPrefix = InetPrefix#6febf6f9
scala> prefix.contains(java.net.InetAddress.getByName("::ffff:1.2.176.20"))
res11: Boolean = true
scala> prefix.contains(java.net.InetAddress.getByName("::ffff:1.2.191.20"))
res12: Boolean = false
But if you calculate that network: (1.2.176.0/20)
$ sipcalc 1.2.176.0/20
-[ipv4 : 1.2.176.0/20] - 0
[CIDR]
Host address - 1.2.176.0
Host address (decimal) - 16953344
Host address (hex) - 102B000
Network address - 1.2.176.0
Network mask - 255.255.240.0
Network mask (bits) - 20
Network mask (hex) - FFFFF000
Broadcast address - 1.2.191.255
Cisco wildcard - 0.0.15.255
Addresses in network - 4096
Network range - 1.2.176.0 - 1.2.191.255
Usable range - 1.2.176.1 - 1.2.191.254
-
I rewrote both (IPv4 and IPv6) in Scala put it for everyone on GitHub. It now also validates within ranges (so /20 etc will be regarded, which the old one did not do.)
You can find the code (i separated it into IPv4 and IPv6) at https://github.com/wasted/scala-util/blob/master/src/main/scala/io/wasted/util/InetPrefix.scala
I also created a blogpost about this.