Tuesday, June 19, 2007

Sorted Hashtable

Need an HashTable uh? Tired of that messy foreach statement ? Here's a simple implementation of a Dictionary collection, but sortable.

namespace snippets101.Collections
{
public enum sortType
{
NoSort = 1,
Keys = 2,
Values = 3
}
public class sHash
{
private ArrayList _alVals;
private ArrayList _alKeys;
private sortType _sort;

public sHash()
{

_alKeys = new ArrayList();
_alVals = new ArrayList();

_sort = sortType.NoSort;

}

public sortType SortType
{
get { return _sort; }
set { _sort = value; }

}

public void Sort()
{
Array k = _alKeys.ToArray(typeof(object));
Array v = _alVals.ToArray(typeof(object));

if (_sort == sortType.Keys)
Array.Sort(k, v, new sHashComparer());
if (_sort == sortType.Values)
Array.Sort(v, k, new sHashComparer());

_alKeys = ArrayList.Adapter(k);
_alVals = ArrayList.Adapter(v);

}

public void Add(object key, object value)
{
if (_alKeys.Contains(key))
throw new Exception("Key already present.");

_alKeys.Add(key);
_alVals.Add(value);

}

public void Clear()
{
_alKeys.Clear();
_alVals.Clear();
}

public bool Contains(object key)
{
return _alKeys.Contains(key);
}

public IDictionaryEnumerator GetEnumerator()
{
return new sHashDictionaryEnumerator(_alKeys, _alVals);
}

public bool IsFixedSize
{
get { return false; }
}

public bool IsReadOnly
{
get { return false; }
}

public ArrayList Keys
{
get { return _alKeys; }
}

public ArrayList Values
{
get { return _alVals; }
}

public void Remove(object key)
{
_alVals.RemoveAt(_alKeys.IndexOf(key));
_alKeys.Remove(key);
}

public object this[object key]
{
get
{
return _alVals[_alKeys.IndexOf(key)];
}
set
{
if (_alKeys.Contains(key))
{
_alVals[_alKeys.IndexOf(key)] = value;
}
else
{
_alKeys.Add(key);
_alVals.Add(value);
}
}
}

public int Count
{
get { return _alKeys.Count; }
}

private class sHashDictionaryEnumerator : IDictionaryEnumerator
{
DictionaryEntry[] items;
Int32 index = -1;

public sHashDictionaryEnumerator(ArrayList keys, ArrayList mappings)
{
items = new DictionaryEntry[keys.Count];
for (int i = 0; i <> {
items[i] = new DictionaryEntry(keys[i],mappings[i]);
}
}

public Object Current { get { ValidateIndex(); return items[index]; } }

public DictionaryEntry Entry
{
get { return (DictionaryEntry)Current; }
}

public Object Key { get { ValidateIndex(); return items[index].Key; } }

public Object Value { get { ValidateIndex(); return items[index].Value; } }

public Boolean MoveNext()
{
if (index < style="color: rgb(51, 51, 255);"> return true; }
return false;
}

private void ValidateIndex()
{
if (index <>= items.Length)
throw new InvalidOperationException("Enumerator is before or after the collection.");
}

public void Reset()
{
index = -1;
}
}

private class sHashComparer : IComparer
{
private bool isNumeric(Type t)
{
switch (t.Name.ToLower())
{
case "int32":
return true;
case "single":
return true;
case "double":
return true;
default:
return false;
}
}


public int Compare(object x, object y)
{

if ((isNumeric(x.GetType())) && (isNumeric(y.GetType())))
{
double d = Math.Truncate(Convert.ToSingle(x) - Convert.ToSingle(y));
if (d <>
return -1;
if (d == 0)
return 0;
if (d > 0)
return 1;

return 0;
}
if ((isNumeric(x.GetType())) && (!isNumeric(y.GetType())))
return -1;
if ((!isNumeric(x.GetType())) && (isNumeric(y.GetType())))
return 1;

return String.Compare(x.ToString(), y.ToString());
}


}


}
}

Monday, June 11, 2007

Dual armed server to server load balancing con Cisco ACE

Let's say you have a large data center, let's say that in this data center you have lots of dual-armed load balanced serverfarms. It could happen that these servers need to call each other's balanced services. Here's how this could be accomplished with very light configuration on real servers.

Scenario :



The BLUE-SERVERFARM real servers needs to query a web service located on the YELLOW-SERVERFARM, on tcp port 2000.
All of the real servers use the "upper" interface (vlan 101) to act as servers, i.e. to answer clients' queries coming from the ACE.
The "internal" interface (vlan 102) is used by the servers when they act as client of someone else's service.
Easy to configure this, matter of routes on the servers. The default gateway is always the ACE, there's a static route on the internal interface for all the ips the server could query acting as a client.


Without configuring Source NAT con the ACE, all connections fail, because of asymmetric response from servers of the YELLOW-SERVERFARM.
When a connection arrives from the ACE, the source ip is the internal interface of the client server. As this ip is on a lan directly connected on the destination server, the response will return over the INTERNAL, not over the same route of the request.



Solution:
Source natting this requests on the ACE will cause the destination server not to know as directly connected the source ip, answering on the default gateway (ACE) and so following the same path of the request.
The simplest way I've found is to reserve a new virtual address only for requests coming from the servers on the same lan, as described above. So clients will continue query the service on the VIP 10.20.0.2 port 2000, while servers on the same lan will query the same service on the same port but on VIP 10.20.0.20, being Source-NATted with an IP from the SNATPOOL.



The real server of the YELLOW-SERVERFARM responding to the request, seeing it from a SNATted address will route the response via the default gateway (ACE) which will send back packets on the same path of the request.

probe tcp 2000-TCP
port 2000
interval 10
passdetect interval 5
passdetect count 1

probe icmp ICMP
interval 10
passdetect interval 5
passdetect count 1

rserver host SERVER-10.1
ip address 10.0.10.1
inservice

rserver host SERVER-10.2
ip address 10.0.10.2
inservice


rserver host SERVER-10.3
ip address 10.0.10.3
inservice

rserver host SERVER-10.4
ip address 10.0.10.4
inservice


serverfarm host BLUE-SERVERFARM_20.1:80
failaction purge
probe ICMP
rserver
SERVER-10.1 80
inservice
rserver SERVER-10.2 80
inservice

serverfarm host YELLOW-SERVERFARM_20.2:2000
failaction purge
probe 2000-TCP
rserver
SERVER-10.3 2000
inservice
rserver SERVER-10.4 2000
inservice


class-map match-all L4-MAP-BLUE-SERVERFARM_20.1:80
2 match virtual-address 10.0.20.1 tcp eq www

class-map match-all L4-MAP-YELLOW-SERVERFARM_20.2:2000
2 match virtual-address 10.0.20.2 tcp eq 2000

class-map match-all L4-SNAT-MAP-YELLOW-SERVERFARM_20.20:2000
2 match virtual-address 10.0.20.20 tcp eq 2000

policy-map type loadbalance first-match L7-BLUE-SERVERFARM_20.1:80
class class-default
serverfarm
BLUE-SERVERFARM_20.1:80

policy-map type loadbalance first-match L7-YELLOW-SERVERFARM_20.2:2000
class class-default
serverfarm
YELLOW-SERVERFARM_20.2:2000

policy-map multi-match L4-POLICYMAPMULTI
class L4-MAP-BLUE-SERVERFARM_20.1:80
loadbalance vip inservice
loadbalance policy
L7-BLUE-SERVERFARM_20.1:80
class L4-MAP-YELLOW-SERVERFARM_20.2:2000
loadbalance vip inservice
loadbalance policy
L7-YELLOW-SERVERFARM_20.2:2000
class L4-MAP-YELLOW-SERVERFARM_20.20:2000
loadbalance vip inservice
loadbalance policy
L7-YELLOW-SERVERFARM_20.2:2000
nat dynamic 1 vlan 101

interface vlan 101
description SERVERSIDE
ip address 10.0.10.199 255.255.255.0
nat-pool 1 10.0.21.1 10.0.21.254 netmask 255.255.255.0
no shutdown
interface vlan 151
description FIREWALLSIDE
ip address 10.0.0.2 255.255.255.240
service-policy input L4-POLICYMAPMULTI
no shutdown



Friday, June 8, 2007

FTP serverfarm on Cisco ACE

This is a mix from Cisco forums and hundreds of trials, being the Cisco ACE Server Load Balancing configuration guide not so clear...

Scenario :



Active FTP :
This is the simpler and it's only a matter of ACE configuration :

probe tcp 21-TCP
port 21
interval 10
passdetect interval 5
passdetect count 1

rserver host SERVER-10.1
ip address 10.0.10.1
inservice

rserver host SERVER-10.2
ip address 10.0.10.2
inservice


serverfarm host FTPFARM_20.1:21
failaction purge
probe 21-TCP
rserver
SERVER-10.1 21
inservice
rserver SERVER-10.2 21
inservice

sticky ip-netmask 255.255.255.255 address source
STICKY-FTPFARM_20.1:21
timeout 20
timeout activeconns
replicate sticky
serverfarm
FTPFARM_20.1:21


class-map match-all L4-MAP-FTPFARM_20.1:21
2 match virtual-address 10.0.20.1 any

policy-map type loadbalance first-match L7-
FTPFARM_20.1:21
class class-default
sticky-serverfarm STICKY-
FTPFARM_20.1:21

policy-map multi-match L4-POLICYMAPMULTI-FTP
class L4-MAP-FTPFARM_20.1:21
loadbalance vip inservice
loadbalance policy
L7-FTPFARM_20.1:21
inspect ftp


interface vlan 101
description SERVERSIDE
ip address 10.0.10.199 255.255.255.0
no normalization
no shutdown
interface vlan 151
description FIREWALLSIDE
ip address 10.0.0.2 255.255.255.240
no normalization
service-policy input L4-POLICYMAPMULTI-FTP
no shutdown


Passive FTP :
In order to make Passive FTP connection work, with the firewall checking consistency of source and destination addresses, youll need to change the FTP server configuration.
On the frox server there's a configuration parameter "PASV Reply Address" that should be set to the VIP (10.0.20.1) in order to have the FTP server call back the client (passive mode) with the same address the firewall see for the active client-server communication.

dotted notation to number

Writing my ip address management software I needed something to order my ips in sql.The dotted notation wasn't so efficient, so, I needed to convert it in an integer.
Wandering on the Net, and with the CCNA study guide open, here's the result, starting from a script read somewhere...


CREATE FUNCTION dbo.IPDottedToNumber( @IPAddress varchar(15))
RETURNS bigint
AS
BEGIN
DECLARE
@biOctetA bigint,
@biOctetB bigint,
@biOctetC bigint,
@biOctetD bigint,
@biIP bigint

DECLARE @tbl TABLE

(
OctetNo smallint,
Octet bigint
)

INSERT INTO @tbl
SELECT ElementID, CONVERT(bigint,Element) FROM dbo.Split(@IPAddress, '.')

IF (SELECT COUNT(*) FROM @tblArray WHERE Octet BETWEEN 0 AND 255) = 4
BEGIN

SET @biOctetA = (SELECT (Octet * 256 * 256 * 256) FROM @tblArray WHERE OctetNo = 1)

SET
@biOctetB = (SELECT (Octet * 256 * 256 ) FROM @tblArray WHERE
OctetNo = 2)

SET
@biOctetC = (SELECT (Octet * 256 ) FROM @tblArray WHERE
OctetNo = 3)

SET
@biOctetD = (SELECT (Octet) FROM @tblArray WHERE
OctetNo = 4)

SET
@biIP = @biOctetA + @biOctetB + @biOctetC + @biOctetD


END


RETURN(@biIP)
END

Thursday, June 7, 2007

You're welcome

As a network engineer and a software developer, I must face technical issues every day, of various complexity. Most of them come from my laziness in reading all of the manual....

However, since the 80% of this issues found a solution by reading something on a forum, a blog, or some code exchange site, I thought it were high time to pay back the favour to Internet.

In this blog I'll put all of the solutions I find (by myself or not), code snippets, configurations, strange things that happens to people like me.

Enjoy.

PS: I'm sure my english is one of the worst you can find around the 'net, but hey! I'm an italian guy, please be patient, I know much better how to cook...