Troubleshoot Voice, Video and Multicast

May 10th, 2010 jud No comments

I have had a hard time figuring out how they are going to test us for voice troubleshooting when the only real command they cover is auto qos and and the MQC. As a result I’m going to concentrate on the definitions.

Voice Definitions
Gatekeeper — provides bandwidth management through call admission control (CAC).
Gateway — ensures interoperability between VOIP and the public switched telephone network (PSTN).
Jitter (delay variation) — When consecutive packets experience different amounts of delay. Data applications tend to be much more forgiving of jitter than voice and video.
Delay — There are multiple types of delay in a network. Some are standard or fixed and some are variable in their affects, the TSHOOT book describes delay as propagation delay, the time it takes to get a bit from one end of a link to the other.
Drops — Congested packets overflow a buffer.

Cisco Phone Boot Process
1. Power, PoE
2. Load firmware from flash.
3. Catalyst switch informs the phone it’s voice VLAN.
4. DHCP for ip address and TFTP server.
5. Downloads configuration using TFTP.
6. Registers with call agent or Call Manager.

QoS Metrics for Video

QoS Metric HQ Audio & Video PC Video Conference Video Surveillance VOIP
One-Way Delay 150 ms 200 ms max 500 ms 150-200 ms1, 2
Jitter4 10 ms 10 ms 10 ms 30 ms
Loss 0.05 % max 0.05 % max 0.5 % max 1.0 % max 3

Sources:
1 — ONT Certification Guide p.62
2 — Cisco DocWiki
3 — Enabling VOIP
4 — TSHOOT Book

Multicasting
Class D IP address in the range 224.0.0.0 through 239.255.255.255. Source sends one packet stream to the multicast address and all hosts that have joined that group receive that packet.

Internet Group Management Protocol (IGMP)
Hosts join a multicast group by sending an IGMP join message to router, which then knows to send multicast messages out that interface. IGMP snooping allows a switch to learn which interfaces desire multicast traffic by listening for IGMP traffic between routers and hosts. This stops the switch from flooding multicast traffic out all ports.

IGMP Version 1 — Hosts join a multicast group by sending a membership report to its local router. Every 60 seconds the querier router sends a messages to all-hosts 224.0.0.1 to ensure that there is a host on that network segment that is still in the group. IGMPv1 does not have a mechanism for hosts to leave a group, and it takes three query intervals (3 minutes) to stop sending multicast traffic to a segment.

IGMP Version 2 — Adds the ability for routers to query a specific multicast group, elect a querier for a segment and allows a host to send a leave group message to the all routers address 224.0.0.2. All routers start as queriers, however, if a router hears a query from another router, the router with the highest IP address on the segment becomes the querier for that segment.

Reverse Path Forwarding (RPF) — Verifies that multicast traffic flows away from the source or root and is flowing toward the branch or host.

Protocol Independent Multicast (PIM) — Allows multicast to build distribution trees regardless of the unicast routing protocol which is running such as EIGRP or OSPF.

PIM Dense Mode (PIM-DM) — Uses a source distribution tree. At first all routers receive traffic for the group, but if no host joins using IGMP the router sends a prune message so that unnecessary traffic does not continue. Most often used when recipients are on every subnet, densely populated.

PIM Sparse Mode (PIM-SM) — Uses a shared tree with a root router or rendezvous point (RP) that is not necessarily the multicast source but is usually centrally located on the network. All multicast streams go through this router, hence the name share tree or shared distribution tree. A router only joins the tree when a host has joined the multicast group. It is built opposite of dense mode, the tree is built from the leaves to the root, it is only when a host joins a multicast group that the router forwards the membership report to the RP.

PIM Sparse-Dense Mode — Allows a router to use sparse or dense-mode or both at the same time. Dense mode is used to flood RP discovery and announcement messages so that the client can find the RP and use the RP to find the multicast server.

Multicast Configuration

switch(config)# ip igmp snooping
switch(config)# ip igmp snooping vlan x

router(config)# ip multicast-routing
router(config)# ip pim {dense-mode | sparse-mode | sparse-dense-mode}
router(config)# ip pim version {1 | 2}

Multicast Troubleshooting
ip igmp join-group — Let’s a router join a group in order to test.
sh ip igmp group — Shows the groups a router has joined.
sh ip igmp interface — Shows IGMP information for each interface.

sh ip mroute
ping multicast address
sh ip pim rp
sh ip rpf

sh ip igmp group

R1#sh ip igmp groups
IGMP Connected Group Membership
Group Address    Interface                Uptime    Expires   Last Reporter   Group Accounted
232.32.32.32     Loopback1                00:06:35  00:02:18  192.168.1.1    
224.0.1.40       Loopback1                00:06:35  00:02:16  192.168.1.1

sh ip igmp interface

R1#sh ip igmp interface
Loopback1 is up, line protocol is up
  Internet address is 192.168.1.1/24
  IGMP is enabled on interface
  Current IGMP host version is 2
  Current IGMP router version is 2
  IGMP query interval is 60 seconds
  IGMP querier timeout is 120 seconds
  IGMP max query response time is 10 seconds
  Last member query count is 2
  Last member query response interval is 1000 ms
  Inbound IGMP access group is not set
  IGMP activity: 2 joins, 0 leaves
  Multicast routing is enabled on interface
  Multicast TTL threshold is 0
  Multicast designated router (DR) is 192.168.1.1 (this system)
  IGMP querying router is 192.168.1.1 (this system)
  Multicast groups joined by this system (number of users):
      224.0.1.40(1)  232.32.32.32(1)

sh ip mroute

R1#sh ip mroute
IP Multicast Routing Table
Flags: D - Dense, S - Sparse, B - Bidir Group, s - SSM Group, C - Connected,
       L - Local, P - Pruned, R - RP-bit set, F - Register flag,
       T - SPT-bit set, J - Join SPT, M - MSDP created entry,
       X - Proxy Join Timer Running, A - Candidate for MSDP Advertisement,
       U - URD, I - Received Source Specific Host Report,
       Z - Multicast Tunnel, z - MDT-data group sender,
       Y - Joined MDT-data group, y - Sending to MDT-data group
Outgoing interface flags: H - Hardware switched, A - Assert winner
 Timers: Uptime/Expires
 Interface state: Interface, Next-Hop or VCD, State/Mode

(*, 232.32.32.32), 00:21:25/stopped, RP 0.0.0.0, flags: DCL
  Incoming interface: Null, RPF nbr 0.0.0.0
  Outgoing interface list:
    Serial1/1, Forward/Dense, 00:21:25/00:00:00
    Serial1/0, Forward/Dense, 00:21:25/00:00:00
    Loopback1, Forward/Dense, 00:21:25/00:00:00

ping multicast address

R1#ping 232.32.32.32    

Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 232.32.32.32, timeout is 2 seconds:

Reply to request 0 from 172.16.102.2, 12 ms
Reply to request 0 from 172.16.103.3, 16 ms
Reply to request 0 from 192.168.1.1, 16 ms
Categories: CCNP TSHOOT, Routing Tags:

There’s a command for that.TM

May 3rd, 2010 jud No comments

Last week we had in a consultant from one of our electronic medical record (EMR) vendors and we were working on a RedHat cluster. He was asking me to check whether a service started at a runlevel and wanted me to ls /etc/rc.d/rc5.d, I looked at him and said, “There’s a command for that.” (The command is chkconfig.) I’ve been chuckling about it for the past few days.

The Linux and BSD foundations need to get together and start an advertising campaign, it would be a great parody.

Categories: Linux, Musings Tags:

TSHOOT Sidenote

May 2nd, 2010 jud No comments

I added some more flashcards in the files directory. These all have a file name of tshoot-topic.csv and are for the Flashcards Deluxe iPhone app.

If you notice any errors in the cards please shoot an email or make a comment. At this point I have not downloaded all of them so there may be serious errors or formatting changes. I made them from my notes so any errors are mine.

Categories: CCNP TSHOOT, Routing Tags:

Troubleshoot DHCP

May 2nd, 2010 jud No comments

I am not going to get into a long discussion of DHCP. I run the DNS/DHCP servers at work and have a pretty good idea of how it all works. Even though the basics are the same, BIND is a different animal than IOS.

The DCHP process from debug messages
–> DHCPD: DHCPDISCOVER received from client
<-- DHCPD: Sending DHCPOFFER to client
–> DHCPD: DHCPREQUEST received from client
<-- DHCPD: Sending DHCPACK to client

And the actual debug output.

R4#debug ip dhcp server packet
DHCP server packet debugging is on.
R4#
*Apr 28 22:51:33.407: DHCPD: DHCPDISCOVER received from client 0063.6973.636f.2d30.3030.342e.6464.3639.2e66.6430.312d.4661.302f.30 through relay 10.2.1.1.
*Apr 28 22:51:33.407: DHCPD: Allocate an address without class information (10.2.1.0)
R4#
*Apr 28 22:51:35.407: DHCPD: Sending DHCPOFFER to client 0063.6973.636f.2d30.3030.342e.6464.3639.2e66.6430.312d.4661.302f.30 (10.2.1.7).
*Apr 28 22:51:35.407: DHCPD: unicasting BOOTREPLY for client 0004.dd69.fd01 to relay 10.2.1.1.
*Apr 28 22:51:35.411: DHCPD: DHCPREQUEST received from client 0063.6973.636f.2d30.3030.342e.6464.3639.2e66.6430.312d.4661.302f.30.
*Apr 28 22:51:35.411: DHCPD: No default domain to append - abort update
*Apr 28 22:51:35.411: DHCPD: Sending DHCPACK to client 0063.6973.636f.2d30.3030.342e.6464.3639.2e66.6430.312d.4661.302f.30 (10.2.1.7).
*Apr 28 22:51:35.411: DHCPD: unicasting BOOTREPLY for client 0004.dd69.fd01 to relay 10.2.1.1.

DHCP Message Table

Message Use
DHCPDISCOVER Host is querying available servers, sent to broadcast address 255.255.255.255 on UDP port 67.
DHCPOFFER Server responds on UDP port 68.
DHCPREQUEST Client broadcasts to a specific DHCP server requesting the offered parameters from that server
DHCPDECLINE Message sent from the client to the server that the address is already in use.
DHCPACK The server sends configuration parameters including network address to a client.
DHCPNAK The server sends a refusal to the client for request for configuration.
DHCPRELEASEClient tells a server it is giving up an address and the remaining lease.
DHCPINFORM A client already has an IP address but is requesting other configuration paramets that the DHCP server is configured to deliver such as DNS or Winbind addresses.

IOS DHCP Server Configuration
The lease command is in days. Also, the excluded-address field can be a range low to high, for instance the snippet below will exclude the range of .1 to .50 on the 10.2.1.x subnet.

ip dhcp excluded-address 10.2.1.1 10.2.1.50

DHCP Server configuration:

no ip dhcp use vrf connected
ip dhcp excluded-address 10.2.1.1
ip dhcp excluded-address 10.2.1.2
ip dhcp excluded-address 10.2.1.254
!
ip dhcp pool vlan10
   network 10.2.1.0 255.255.255.0
   default-router 10.2.1.1
   lease 5

DHCP Helper Address
DHCP uses broadcast address because the client does not have an IP address with which to communicate. Routers do not forward broadcasts, therefore routers need to set up to forward DHCP traffic, the IP helper-address command also forwards the following protocols:
TFTP
Domain Name System (DNS)
Internet Time Service (ITS)
NetBIOS name server
NetBIOS datagram server
BootP
TACACS

interface Vlan10
 ip address 10.2.1.1 255.255.255.0
 ip helper-address 10.1.4.5

An IOS router as a DHCP client:

interface FastEthernet0/0
 ip address dhcp

DHCP Troubleshooting
show ip dhcp conflict
show ip dhcp binding

R4#sh ip dhcp binding
Bindings from all pools not associated with VRF:
IP address          Client-ID/          Lease expiration        Type
            Hardware address/
            User name
10.2.1.5            0063.6973.636f.2d30.    May 06 2010 11:16 AM    Automatic
                    3064.302e.6262.6566.
                    2e36.3463.312d.4661.
                    302f.30
10.2.1.8            0063.6973.636f.2d30.    May 07 2010 07:09 PM    Automatic
                    3030.342e.6464.3639.
                    2e66.6430.312d.4661.
                    302f.30

clear ip dhcp binding *
This is not necessarily the best command to run on a production router because you clear the addresses the router knows it has given out.
clear ip dhcp conflict *
debug ip dhcp server packet
See the listing above.
debug ip dhcp server events

R4#debug ip dhcp server events
DHCP server event debugging is on.
R4#
*May  2 19:07:21.660: DHCPD: checking for expired leases.
R4#
*May  2 19:07:59.764: DHCPD: Sending notification of TERMINATION:
*May  2 19:07:59.764:  DHCPD: address 10.2.1.7 mask 255.255.255.0
*May  2 19:07:59.764:  DHCPD: reason flags: RELEASE
*May  2 19:07:59.764:   DHCPD: htype 1 chaddr 0004.dd69.fd01
*May  2 19:07:59.764:   DHCPD: lease time remaining (secs) = 315841
*May  2 19:07:59.764: DHCPD: returned 10.2.1.7 to address pool vlan10.
R4#
*May  2 19:09:04.224: DHCPD: Sending notification of DISCOVER:
*May  2 19:09:04.224:   DHCPD: htype 1 chaddr 0004.dd69.fd01
*May  2 19:09:04.224:   DHCPD: remote id 020a00000a01040500000000
*May  2 19:09:04.224:   DHCPD: circuit id 00000000
*May  2 19:09:04.224: DHCPD: Seeing if there is an internally specified pool class:
*May  2 19:09:04.224:   DHCPD: htype 1 chaddr 0004.dd69.fd01
*May  2 19:09:04.224:   DHCPD: remote id 020a00000a01040500000000
*May  2 19:09:04.224:   DHCPD: circuit id 00000000
R4#
*May  2 19:09:06.224: DHCPD: Adding binding to radix tree (10.2.1.8)
*May  2 19:09:06.224: DHCPD: Adding binding to hash tree
*May  2 19:09:06.224: DHCPD: assigned IP address 10.2.1.8 to client 0063.6973.636f.2d30.3030.342e.6464.3639.2e66.6430.312d.4661.302f.30.
*May  2 19:09:06.228: DHCPD: Sending notification of ASSIGNMENT:
*May  2 19:09:06.228:  DHCPD: address 10.2.1.8 mask 255.255.255.0
*May  2 19:09:06.228:   DHCPD: htype 1 chaddr 0004.dd69.fd01
*May  2 19:09:06.228:   DHCPD: lease time remaining (secs) = 432000
R4#
*May  2 19:09:21.660: DHCPD: checking for expired leases.

sh ip dhcp pool

R4#sh ip dhcp pool

Pool vlan10 :
 Utilization mark (high/low)    : 100 / 0
 Subnet size (first/next)       : 0 / 0
 Total addresses                : 254
 Leased addresses               : 1
 Pending event                  : none
 1 subnet is currently in the pool :
 Current index        IP address range                    Leased addresses
 10.2.1.10            10.2.1.1         - 10.2.1.254        1

Additional Source:
Document ID: 27470

Categories: CCNP TSHOOT, Routing Tags:

Troubleshoot NAT

April 27th, 2010 jud No comments

Definitions
NAT Types
Static NAT — A one-to-one mapping of private to public IP addresses, best used for a device that needs access from outside the AS.
Dynamic NAT — A dynamic one-to-one mapping between private and public IP addresses, however, the mapping can vary and depends upon the addresses left in the pool.
NAT Overloading — PAT, allows multiple private addresses to masquerade as one public IP address by using layer 4 port numbers to differentiate sessions.
Overlapping NAT — Used when the same subnets are in use in two locations and addresses overlap.

NAT Address Types
Inside Local — A private address referencing an inside device.
Inside Global — A public address referencing an inside device.
Outside Local — A private address referencing an outside device.
Outside Global — A public address referencing an outside device.
The TSHOOT book had a good mnemonic that helps everything else fall in place, global starts with g, it means good, good being a routable address on the internet.

Order of operation for an interface, inside to outside network.
1. Decryption of IPsec traffic
2. Input ACL applied
3. Input policing applied
4. Input accounting applied
5. Policy-based routing (PBR)
6. Redirecting traffic to a web cache
7. NAT translating local to global addresses
8. Crypto map application
9. Output ACL applied
10. Cisco IOS Firewall inspection performed
11. TCP intercept feature applied
12. Encryption performed

Order of operation for an interface, outside to inside network.
1. Decryption of IPsec traffic
2. Input ACL applied
3. Input policing applied
4. Input accounting applied
5. NAT translating global to local addresses
6. Policy Based Routing (PBR)
7. Redirecting traffic to a web cache
8. Crypto map application
9. Output ACL applied
10. Cisco IOS Firewall inspection performed
11. TCP intercept feature applied
12. Encryption performed

Troubleshoot NAT
show ip nat statistics
Displays general NAT information of the router.

R1#sh ip nat statistics
Total active translations: 2 (0 static, 2 dynamic; 2 extended)
Outside interfaces:
  Serial0/1/0.15
Inside interfaces:
  Serial0/0/0.12
Hits: 45  Misses: 5
CEF Translated packets: 50, CEF Punted packets: 0
Expired translations: 3
Dynamic mappings:
-- Inside Source
[Id: 1] access-list 10 pool WAN refcount 2
 pool WAN: netmask 255.255.255.252
    start 209.65.200.225 end 209.65.200.225
    type generic, total addresses 1, allocated 1 (100%), misses 0
Appl doors: 0
Normal doors: 0
Queued Packets: 0

sh ip nat translations
Shows the current translations on the router. These can be reset with the command clear ip nat translation *.

R1#sh ip nat translations
Pro Inside global      Inside local       Outside local      Outside global
icmp 209.65.200.225:7  10.1.1.10:7        209.65.200.241:7   209.65.200.241:7

debug ip nat
Shows real time source and destination of NAT sessions on the router.

R1#debug ip nat
IP NAT debugging is on
R1#
*Apr 27 23:29:54.137: NAT*: s=10.1.1.10->209.65.200.225, d=209.65.200.241 [31]
*Apr 27 23:29:54.169: NAT*: s=209.65.200.241, d=209.65.200.225->10.1.1.10 [31]
*Apr 27 23:29:54.253: NAT*: s=10.1.1.10->209.65.200.225, d=209.65.200.241 [32]
*Apr 27 23:29:54.281: NAT*: s=209.65.200.241, d=209.65.200.225->10.1.1.10 [32]
*Apr 27 23:29:54.369: NAT*: s=10.1.1.10->209.65.200.225, d=209.65.200.241 [33]
*Apr 27 23:29:54.397: NAT*: s=209.65.200.241, d=209.65.200.225->10.1.1.10 [33]
*Apr 27 23:29:54.485: NAT*: s=10.1.1.10->209.65.200.225, d=209.65.200.241 [34]
*Apr 27 23:29:54.513: NAT*: s=209.65.200.241, d=209.65.200.225->10.1.1.10 [34]
R1#
*Apr 27 23:29:54.601: NAT*: s=10.1.1.10->209.65.200.225, d=209.65.200.241 [35]
*Apr 27 23:29:54.629: NAT*: s=209.65.200.241, d=209.65.200.225->10.1.1.10 [35]
Categories: CCNP TSHOOT, Routing Tags:

Troubleshoot Security

April 26th, 2010 jud No comments

There are three planes of a router that need to be secured, the management plane, control plane and data plane.

Management Plane
Used to access and configure a switch or router. It is secured through SNMPv3, TACACS+, VTY ACLs and SSH. It is also a best practice to have role based CLI views.

TACACS+ and RADIUS are used in part to secure user access to the management plane, the major differences between them:

Characteristic TACACS+ RADIUS
Transport TCP UDP
Modularity Separates authentication, authorization and accounting Combines authentication and authorization
Security Limit commands No command limit
Encryption Entire packet Only encrypts password
Accounting Basic Robust
Standard No Yes

Sources:
Cisco Document ID 13838
TSHOOT Book p.287

Control plane
Includes routing protocols and spanning tree used between routers and switches, it is the ability of a router to route. The control plane can be secured by the command auto secure, routing protocol authentication, and CPU/memory thresholding.

See each routing protocol discussion for troubleshooting steps.

Securing STP
Root Guard — Is enabled on a per-port basis. When a port receives a superior BPDU, with a lower bridge ID, the local switch will not allow the new switch to become the root. Instead the port is changed to root-inconsistent state, no data can be sent or received until the BPDUs stop.

BPDU Guard — PortFast moves an end-user port to forwarding state without going through all of the STP checks and can induce loops in the network. If any BPDU is received on a port where BPDU guard is enabled that port is put into errdisable state. It can then be recovered manually or through the errdisable timeout function.

Data plane
Forwards data through a router or switch. The data plane can be secured through ACLs, 802.1x, Unicast Reverse Path Forwarding (uRPF), IPsec VPN tunnels.

Securing DHCP and ARP:
DHCP snooping — With DHCP snooping enabled a switch port is either trusted or untrusted. Any DHCP replies coming from an untrusted port are discarded because they must have come from a rogue DHCP server. Additionally that switch port is shut in the errdisable state.

Dynamic ARP Inspection (DAI) — Helps to prevent ARP spoofing attacks. DHCP snooping keeps track of completed DHCP bindings including MAC address, IP address offered and lease time. This database is used by DAI to stop man-in-the-middle style attacks.

802.1X
Port-based authentication is a combination of AAA authentication and port security. An 802.1x port begins in an unauthorized state and requires the client to authenticate before it is allowed to communicate. The three components of an 802.1x network are:
Supplicant — Device trying to gain access.
Authenticator — Acts as an intermediary (proxy) between the host and the authentication server, requesting identity information from the host, verifying that information with the authentication server, and relaying a response to the host.
Authentication Server — Performs the actual authentication of the supplicant. The authentication server validates the identity of the supplicant and notifies the switch if it is authorized to communicate on the network.
Source:
TSHOOT Book
6500 802.1X Configuration Guide

Categories: CCNP TSHOOT, Routing Tags:

Doping in Cycling

April 26th, 2010 jud No comments

Today I read about Vino’s unpopular win at L-B-L. That is fine, we can all sit and wonder why or how, but there was no outrage about Valverde getting third. Valverde could not compete in the 2008 TdF because it went through Italy and the Italians had proved he doped through a genetic match in the Puerto Affair. Now the UCI is trying get a worldwide ban on Valverde racing but no one cares.

On the same front page where they are excoriating the win by Vino they also state that Valverde’s Italian ban has been confirmed by Swiss courts. Why is Valverde still racing? Where is the outrage? Why are fans not complaining that Valverde was caught doping, has been trying to get off on a technicality, and is still racing?

Boggles the mind. Whether or not Vino is contrite has nothing to do with him paying the price. He was suspended for two years, did his time and is back racing. Valverde should not be racing at all.

Tailwinds.

Categories: Cycling, Musings Tags:

DocMgr with AD Authentication

April 23rd, 2010 jud No comments

Over the past couple of days I hacked an older version of DocMgr to authenticate users to Active Directory. I thought it might be interesting for some readers to see my thought process, hence this post.

All of the scripts in this post along with my test directory and my changes to DocMgr can be downloaded here.

First I started out with a simple php form for authentication. The input portion is in the download, this is the processing side. Some interesting notes about this script. At first I was using the full distinguished name (DN) of the user but that doesn’t scale. It was not until I downloaded adLDAP that I saw you could just use username@domain.org to authenticate. I don’t know why I never tried, but that stymied me.

The reason this script looks for groups the user belongs to is because originally I figured I would limit access by group. The problem is that DocMgr has it’s own permission system and you must be in the database in order to be able to login. The script at the bottom is how I dealt with that limitation.

The ldpad_set_option and ldap_get_option calls were for testing once I began hacking DocMgr. I added them in to test an error I was getting in ldap_search.

<?php
session_start();
header("Cache-control: private"); //IE 6 Fix

$username=$_POST['username'];
$password=$_POST['password'];
$ds;
$searchbase = 'OU=Users,DC=CIRCUS,DC=ORG';
$group = 'CN=DocMgr,OU=Users,DC=CIRCUS,DC=ORG';

echo "<html> <head>  <title>AD Test</title></head> <body>";
echo "<p align=\"left\">This is testAD</p>\n";
echo "Username: ". $username . "<br>\n";
echo "Password: ". $password . "<br>\n";

function ad_ldap_auth()
{
    global $username, $password, $ds;
    $ldapServer = 'server.circus.org';
    $ldapPort = '389';
    $ds = ldap_connect($ldapServer, $ldapPort)
        or die("Could not connect to ldap.");

    ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);

    if ($ds)
    {
        $binddn = "$username@.circus.org";
        $ldapbind = ldap_bind($ds, $binddn, $password);

        if ($ldapbind)
        {
                echo "Bound<br>\n";                  
        }else{
                echo "<br>Not bound<br>\n";                  
        }
    }
    return $ldapbind;
}


$test = ad_ldap_auth();

if ($test)
{
    $_SESSION['name'] = $username;
    $_SESSION['expire'] = (time() + 3600);

    $filter = "sAMAccountName=$username";
    //$filter = "sAMAccountName=*";
    $attrib = array("memberOf");
    $attrsonly = 0;
    $sizelimit = 3;
    $timelimit = 10;

    ldap_get_option($ds, LDAP_OPT_SIZELIMIT, $optVal); // returns 0
    echo "size_limit =". $optVal ."<br>";
   
    ldap_set_option($ds, LDAP_OPT_SIZELIMIT, 5); // returns TRUE
    ldap_get_option($ds, LDAP_OPT_SIZELIMIT, $optVal); // returns 0
    echo "size_limit =". $optVal ."<br>";

    $sr = ldap_search($ds, $searchbase, $filter, $attrib);

    for ($entry=ldap_first_entry($ds,$sr); $entry!=false; $entry=ldap_next_entry($ds,$entry))
    {
        $values = ldap_get_values($ds, $entry, "memberOf");

        echo $values["count"] . " groups for this entry.<br />";

        for ($i=0; $i < $values["count"]; $i++)
        {
                echo $values[$i] . "<br />";
            if (strcmp($values[$i],$group) == 0)
            {
                echo "User is member.<br>";
            }
        }
    }
} else {
    echo "LDAP Failed.";
}

ldap_unbind($ds);

?>

After finishing my script it was time to move on to DocMgr. I turned on DEBUG in the config.php and began placing debug output throughout DogMgr so that I could see the flow of function calls as the program ran.

if (DEBUG)
   print("authroized == 1<br>");

I wrote down the flow of some key authentication functions and then began reading them to understand what each did and where I should start hacking. Then I went and broke the old ldap includes into open-ldap and ad-ldap includes and set up the ifdefs in the different files. Doing this I was able to still use my OpenLDAP authentication and could hack on ad-ldap.

I set up the ad-ldap-config.php file by comparing the output of ldapsearch in ldif format on both OpenLDAP and AD. We made some changes to the AD population script and moved forward using employeeNumber instead of uidNumber.

After I got the correct entries in AD and OpenLDAP figured out it was just a matter of hacking my test script into the correct functions and testing. There were a couple of errors that I needed to fix. The first was caused by searching by for sAMAccountName=* in the else portion of the ldap_account_search function of docmgr/include/ad_ldap.inc.php.

This was the first error and fix:

Warning: ldap_search(): Partial search results returned: Sizelimit exceeded in /var/www/docmgr/include/ad_ldap.inc.php on line 697

ldap_set_option($ds, LDAP_OPT_SIZELIMIT, 5);

This was the second error and fix:

Warning: ldap_search(): Search: Operations error in /var/www/docmgr/include/ad_ldap.inc.php

ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

Once I finished making DocMgr authenticate to AD I wanted to make the integration into AD complete so that I did not have to manually add or remove users within the web interface. The following is the script that does that.

It is run every morning from cron.

# crontab -l
# m h  dom mon dow   command
# Add/remove DocMgr users according to AD group DocMgr.
16 4 * * * /usr/local/bin/ad-docmgr.pl

And the code for allowing access to DocMgr. I truncate the table and just add the users each time.

#!/usr/bin/perl

# 2010-04-13  Jud Bishop
# This script queries active directory for every member of the DocMgr group
# and adds them to the auth_accoutperm table, which gives them the ability
# to login to DocMgr after authenicationg to AD.

use strict;
use DBI;
use Net::LDAP;
use Net::LDAP::LDIF;
use Net::LDAP::Entry;

# Globals
# 1 == debug
# 0 == quiet
my $debug = 1;

sub connectLDAP {
    if($debug){print "connectLDAP\n";}

    my $ldap= Net::LDAP->new("server.circus.org", port =>"389", version =>"3" )
        or die $!;
   
    my $result = $ldap->bind( "CN=SpecialUser,DC=CIRCUS,DC=ORG",
                password => "PassWord" );
        die $result->error() if $result->code();

    return $ldap;
}

sub queryORG {

    my $mesg;
    my ($ldap, $dbh) = @_;

    if($debug){print "queryORG\n";}

    $mesg = $ldap->search(
        base => "OU=Users,DC=CIRCUS,DC=ORG",
        scope => "sub",
        filter => "(memberOf = CN=DocMgr,OU=Groups,DC=CIRCUS,DC=ORG)");
   
    if ( $mesg->count() > 0 )
    {
            my $max = $mesg->count;
                for ( my $j = 0 ; $j < $max ; $j++ )
                {
            my $entry = $mesg->entry ( $j );
            foreach my $t ( $entry->get_value( "employeeNumber" ))
                    {
                if($debug){print "employeeNumber $t\n";}
                insertQuery($dbh, $t);
            }
        }
    }
}

sub unbindLDAP {
    if($debug){print "unbindLDAP\n";}
    my $ldap = shift;
    $ldap->unbind();

}

sub connectPostgres {
    if($debug){print "connectPostgres\n";}

    my $dbh = DBI->connect('DBI:Pg:dbname=docmgr;host=127.0.0.1', 'DocMgrUserName', 'PassWord' , {
        PrintError => 0,
        RaiseError => 1 # Report errors via die(), needs less error handling because
                # DBI will check for us and die() automagically.
    });

    if ($dbh) {
        # From above we don't have to check, this is just for debugging.
        if($debug){print "connected\n";}
        return $dbh;
    }
}

# Truncate the table so we don't have to worry about who is there and who isn't.
sub deleteQuery {
    if($debug){print "deleteQuery\n";}

    my $dbh = shift;
    my $sth = $dbh->prepare("TRUNCATE auth_accountperm");
    $sth->execute;
}

sub insertQuery {
    if($debug){print "insertQuery\n";}

    my ($dbh, $employeeNumber) = @_;
   
    my $sth = $dbh->prepare("INSERT INTO auth_accountperm VALUES ($employeeNumber, 1, true)");

    $sth->execute;
    if($debug){print "INSERT INTO auth_accountperm VALUES ($employeeNumber, 1, true)\n"};
}

sub disconnectPostgres {
    if($debug){print "disconnectPostgres\n";}
   
    my $dbh = shift;
    $dbh->disconnect();
}

my $ldap = connectLDAP();

my $dbh = connectPostgres();

deleteQuery($dbh);

queryORG($ldap,$dbh);

disconnectPostgres($dbh);

unbindLDAP ($ldap);

# end main
Categories: Code, Linux Tags:

Quotes I Enjoy

April 22nd, 2010 jud No comments

I wish I knew who to attribute the quotes who have no attribution. If you know the author, please contact me. These are not my own. I will update this occasionally.

Associates do a task until they get it right, experts do a task until they can not do it wrong.
— Relevant to CCNA vs. CCIE.

I’m confronted by my own ignorance or misunderstandings all the time.
— Anthony Bourdain

The worst elements in a group reflect on everyone who chooses to associate with them.
— mtbr.com

In the modern world the stupid are cocksure while the intelligent are full of doubt.
— Bertrand Russell, this is known as the Dunning-Kruger effect.

Never attribute to malice what can be adequately explained by incompetence.
— Common saying

We are what we repeatedly do, therefore excellence is not by chance but a habit.
— I have this on my desk at work. Why settle for being mediocre.

When your IQ is higher than your body weight, you can’t always find what you need at the family bookstore.
— Chris MacAskill, SmugMug.com

The patent system should not be a roulette wheel.
— Richard Silver

If it ain’t broke, you’re not trying hard enough.
— Google Test Blog

A person who never made a mistake never tried anything new.
— Albert Einstein

Don’t worry about people stealing your ideas. If your ideas are any good, you’ll have to ram them down people’s throats.
— Howard Aiken

Strive not to be a success, but rather to be of value.
— Albert Einstein

Open source asks “How can I fix this.” Closed source asks, “How can you fix this for me.”
— Richard Silver

Information is not knowledge. The only source of knowledge is experience.
— Albert Einstein

Profit is accounting opinion, but cash is fact.
— Chris MacAskill, SmugMug.com

Categories: Musings Tags:

Linux DBI::ODBC AS400

April 19th, 2010 jud No comments

At the Circus we are removing the last vestige of our OpenLDAP implementation and moving it to Active Directory. As a result I’m going to document some of the odd scripts I have written as glue to help keep things running. Some of the scripts are too long to document and will just be kept in my script library, while others like the ones below, are generic enough that they might help someone else.

This is some documentation for how to query an AS400 from Linux. When I did this project I actually talked to one of the developers of the iSeries Access programs for help. He ended up sending me a snapshot rpm to get it all working.

Here are the configuration files.

#cat /etc/odbcinst.ini

[iSeries Access ODBC Driver]
Description     = iSeries Access for Linux ODBC Driver
Driver          = /opt/ibm/iSeriesAccess/lib/libcwbodbc.so
Setup           = /opt/ibm/iSeriesAccess/lib/libcwbodbcs.so
Threading       = 2
DontDLClose     = 1
UsageCount      = 1
#cat /etc/odbc.ini

[AS400]
Description     = iSeries Access ODBC Driver
Driver          = iSeries Access ODBC Driver
System          = as400.circus.org
UserID          = UserName
Password        = Password
Naming          = 0
DefaultLibraries= QGPL
Database        =
ConnectionType  = 0
CommitMode      = 2
ExtendedDynamic = 1
DefaultPkgLibrary = QGPL
DefaultPackage   = A/DEFAULT(IBM),2,0,1,0,512
AllowDataCompression = 1
LibraryView     = 0
AllowUnsupportedChar = 0
ForceTranslation= 0
Trace           = 1
DSN             = AS400

This is a simple script to list the drivers available on the system. This does not look like a script I wrote so I am reticent to take ownership.

#!/usr/bin/perl

use DBI;

my @drivers = DBI->available_drivers();

die "No drivers found. \n" unless @drivers;

# list the data sources
foreach my $driver ( @drivers )
{
    print "Driver: $driver\n";
    my @dataSources = DBI->data_sources( $driver );
    foreach my $dataSource ( @dataSources )
    {
        print "\tData Source: $dataSource\n";
    }
    print "\n";
}

exit

And the resulting output from the script above.

# perl drivers.pl
Driver: DBM
    Data Source: DBI:DBM:f_dir=PHPDemo
    Data Source: DBI:DBM:f_dir=SQL-LDAP
    Data Source: DBI:DBM:f_dir=.
    Data Source: DBI:DBM:f_dir=DBD-ODBC-1.09
    Data Source: DBI:DBM:f_dir=unixODBC-2.2.8
    Data Source: DBI:DBM:f_dir=DBI-1.42

Driver: ExampleP
    Data Source: dbi:ExampleP:dir=.

Driver: File
    Data Source: DBI:File:f_dir=PHPDemo
    Data Source: DBI:File:f_dir=SQL-LDAP
    Data Source: DBI:File:f_dir=.
    Data Source: DBI:File:f_dir=DBD-ODBC-1.09
    Data Source: DBI:File:f_dir=unixODBC-2.2.8
    Data Source: DBI:File:f_dir=DBI-1.42

Driver: ODBC
    Data Source: DBI:ODBC:AS400

This is a simple script to query an AS400. You will have to get with your AS400 administrator because the the query is actually an odd series of fields, at least it was for our organization. I just wrote this to learn how to query the AS400, it was never used in production.

#!/usr/bin/perl

# 2005-09-12 Jud Bishop
# Released under the New BSD License.

# This script uses the DBI::ODBC driver to interact with an AS 400
# database.  Make sure that you use a system DSN rather than a
# user defined one or you will have trouble later.

use strict;
use DBI;

my $UID="UserName";
my $PW="Password";
my $DSN="AS400";


print "connect\n";
my $dbh = DBI->connect ( "dbi:ODBC:$DSN", $UID, $PW, {
#   LongTruncOk => 1
    PrintError => 1
})
    or die "connection failed to database $DBI::errstr\n";

## Set up tracing
print "trace\n";
unlink 'trace.log' if -e 'trace.log';
DBI->trace( 2, 'trace.log' );

## Prepare the SQL statement for execution
print "prepare\n";
my @t = localtime(time);
my $sql_st = $dbh->prepare( " SELECT ALL X FROM Y WHERE I = J ORDER BY Z ");

print "execute\n";
$sql_st->execute()
    or die "Cannot execute SQL statement $DBI::errstr\n";

my @row;
while ( @row = $sql_st->fetchrow_array() )
{
    print "@row\n";
}
warn "Data fetch terminated by error $DBI::errstr\n"
    if $DBI::err;

$dbh->disconnect or warn "disconnection failed $DBI::errstr\n";

exit
Categories: Code, Linux Tags: