freebsd-skq/sys/netatm/uni/uniarp_input.c
2002-03-20 08:00:54 +00:00

876 lines
19 KiB
C

/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $FreeBSD$
*
*/
/*
* ATM Forum UNI Support
* ---------------------
*
* UNI ATMARP support (RFC1577) - Input packet processing
*
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_vc.h>
#include <netatm/atm_ioctl.h>
#include <netatm/atm_sigmgr.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <netatm/ipatm/ipatm_var.h>
#include <netatm/ipatm/ipatm_serv.h>
#include <netatm/uni/uniip_var.h>
#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif
/*
* Local functions
*/
static void proc_arp_req(struct ipvcc *, KBuffer *);
static void proc_arp_rsp(struct ipvcc *, KBuffer *);
static void proc_arp_nak(struct ipvcc *, KBuffer *);
static void proc_inarp_req(struct ipvcc *, KBuffer *);
static void proc_inarp_rsp(struct ipvcc *, KBuffer *);
/*
* Local variables
*/
static Atm_addr satm;
static Atm_addr satmsub;
static Atm_addr tatm;
static Atm_addr tatmsub;
static struct in_addr sip;
static struct in_addr tip;
/*
* Process ATMARP Input Data
*
* Arguments:
* tok uniarp connection token (pointer to ipvcc)
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
void
uniarp_cpcs_data(tok, m)
void *tok;
KBuffer *m;
{
struct ipvcc *ivp = tok;
struct atmarp_hdr *ahp;
KBuffer *n;
int len, plen = sizeof(struct atmarp_hdr);
#ifdef DIAGNOSTIC
if (uniarp_print)
uniarp_pdu_print(ivp, m, "receive");
#endif
/*
* Verify IP's VCC state
*/
if (ivp->iv_state != IPVCC_ACTIVE) {
goto bad;
}
/*
* Get the fixed fields together
*/
if (KB_LEN(m) < sizeof(struct atmarp_hdr)) {
KB_PULLUP(m, sizeof(struct atmarp_hdr), m);
if (m == NULL)
goto bad;
}
KB_DATASTART(m, ahp, struct atmarp_hdr *);
/*
* Initial packet verification
*/
if ((ahp->ah_hrd != htons(ARP_ATMFORUM)) ||
(ahp->ah_pro != htons(ETHERTYPE_IP)))
goto bad;
/*
* Verify/gather source address fields
*/
if ((len = (ahp->ah_shtl & ARP_TL_LMASK)) != 0) {
if (ahp->ah_shtl & ARP_TL_E164) {
if (len > sizeof(struct atm_addr_e164))
goto bad;
satm.address_format = T_ATM_E164_ADDR;
} else {
if (len != sizeof(struct atm_addr_nsap))
goto bad;
satm.address_format = T_ATM_ENDSYS_ADDR;
}
satm.address_length = len;
if (KB_COPYDATA(m, plen, len, (caddr_t)satm.address))
goto bad;
plen += len;
} else {
satm.address_format = T_ATM_ABSENT;
satm.address_length = 0;
}
if ((len = (ahp->ah_sstl & ARP_TL_LMASK)) != 0) {
if (((ahp->ah_sstl & ARP_TL_TMASK) != ARP_TL_NSAPA) ||
(len != sizeof(struct atm_addr_nsap)))
goto bad;
satmsub.address_format = T_ATM_ENDSYS_ADDR;
satmsub.address_length = len;
if (KB_COPYDATA(m, plen, len, (caddr_t)satmsub.address))
goto bad;
plen += len;
} else {
satmsub.address_format = T_ATM_ABSENT;
satmsub.address_length = 0;
}
if ((len = ahp->ah_spln) != 0) {
if (len != sizeof(struct in_addr))
goto bad;
if (KB_COPYDATA(m, plen, len, (caddr_t)&sip))
goto bad;
plen += len;
} else {
sip.s_addr = 0;
}
/*
* Verify/gather target address fields
*/
if ((len = (ahp->ah_thtl & ARP_TL_LMASK)) != 0) {
if (ahp->ah_thtl & ARP_TL_E164) {
if (len > sizeof(struct atm_addr_e164))
goto bad;
tatm.address_format = T_ATM_E164_ADDR;
} else {
if (len != sizeof(struct atm_addr_nsap))
goto bad;
tatm.address_format = T_ATM_ENDSYS_ADDR;
}
tatm.address_length = len;
if (KB_COPYDATA(m, plen, len, (caddr_t)tatm.address))
goto bad;
plen += len;
} else {
tatm.address_format = T_ATM_ABSENT;
tatm.address_length = 0;
}
if ((len = (ahp->ah_tstl & ARP_TL_LMASK)) != 0) {
if (((ahp->ah_tstl & ARP_TL_TMASK) != ARP_TL_NSAPA) ||
(len != sizeof(struct atm_addr_nsap)))
goto bad;
tatmsub.address_format = T_ATM_ENDSYS_ADDR;
tatmsub.address_length = len;
if (KB_COPYDATA(m, plen, len, (caddr_t)tatmsub.address))
goto bad;
plen += len;
} else {
tatmsub.address_format = T_ATM_ABSENT;
tatmsub.address_length = 0;
}
if ((len = ahp->ah_tpln) != 0) {
if (len != sizeof(struct in_addr))
goto bad;
if (KB_COPYDATA(m, plen, len, (caddr_t)&tip))
goto bad;
plen += len;
} else {
tip.s_addr = 0;
}
/*
* Verify packet length
*/
for (len = 0, n = m; n; n = KB_NEXT(n))
len += KB_LEN(n);
if (len != plen)
goto bad;
/*
* Now finish with packet-specific processing
*/
switch (ntohs(ahp->ah_op)) {
case ARP_REQUEST:
proc_arp_req(ivp, m);
break;
case ARP_REPLY:
proc_arp_rsp(ivp, m);
break;
case INARP_REQUEST:
proc_inarp_req(ivp, m);
break;
case INARP_REPLY:
proc_inarp_rsp(ivp, m);
break;
case ARP_NAK:
proc_arp_nak(ivp, m);
break;
default:
goto bad;
}
return;
bad:
uniarp_stat.uas_rcvdrop++;
if (m)
KB_FREEALL(m);
}
/*
* Process an ATMARP request packet
*
* Arguments:
* ivp pointer to input VCC's IPVCC control block
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
static void
proc_arp_req(ivp, m)
struct ipvcc *ivp;
KBuffer *m;
{
struct ip_nif *inp;
struct atm_nif *nip;
struct siginst *sgp;
struct uniip *uip;
struct uniarp *uap;
struct in_addr myip;
int s = splnet();
/*
* Only an arp server should receive these
*/
inp = ivp->iv_ipnif;
nip = inp->inf_nif;
uip = (struct uniip *)inp->inf_isintf;
if ((uip == NULL) ||
(uip->uip_arpstate != UIAS_SERVER_ACTIVE))
goto drop;
/*
* These should be sent only on SVCs
*/
if ((ivp->iv_flags & IVF_SVC) == 0)
goto drop;
/*
* Locate our addresses
*/
sgp = nip->nif_pif->pif_siginst;
myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr;
/*
* Target IP address must be present
*/
if (tip.s_addr == 0)
goto drop;
/*
* Drop packet if both Source addresses aren't present
*/
if ((sip.s_addr == 0) || (satm.address_format == T_ATM_ABSENT))
goto drop;
/*
* Source addresses can't be ours
*/
if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) &&
ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) {
struct vccb *vcp = ivp->iv_conn->co_connvc->cvc_vcc;
log(LOG_WARNING,
"uniarp: vcc=(%d,%d) reports our ATM address\n",
vcp->vc_vpi, vcp->vc_vci);
goto drop;
}
if (sip.s_addr == myip.s_addr) {
struct vccb *vcp = ivp->iv_conn->co_connvc->cvc_vcc;
log(LOG_WARNING,
"uniarp: vcc=(%d,%d) reports our IP address\n",
vcp->vc_vpi, vcp->vc_vci);
goto drop;
}
/*
* Validate Source IP address
*/
if (uniarp_validate_ip(uip, &sip, UAO_REGISTER) != 0)
goto drop;
/*
* If the source and target IP addresses are the same, then this
* must be a client registration request (RFC-2225). Otherwise,
* try to accomodate old clients (per RFC-2225 8.4.4).
*/
if (sip.s_addr == tip.s_addr)
(void) uniarp_cache_svc(uip, &sip, &satm, &satmsub,
UAO_REGISTER);
else {
uap = (struct uniarp *)ivp->iv_arpent;
if ((uap == NULL) || (uap->ua_origin < UAO_REGISTER))
(void) uniarp_cache_svc(uip, &sip, &satm, &satmsub,
UAO_REGISTER);
}
/*
* Lookup the target IP address in the cache (and also check if
* the query is for our address).
*/
UNIARP_LOOKUP(tip.s_addr, uap);
if (uap && (uap->ua_flags & UAF_VALID)) {
/*
* We've found a valid mapping
*/
(void) uniarp_arp_rsp(uip, &uap->ua_arpmap, &sip, &satm,
&satmsub, ivp);
} else if (tip.s_addr == myip.s_addr) {
/*
* We're the target, so respond accordingly
*/
(void) uniarp_arp_rsp(uip, &uip->uip_arpsvrmap, &sip, &satm,
&satmsub, ivp);
} else {
/*
* We don't know who the target is, so NAK the query
*/
(void) uniarp_arp_nak(uip, m, ivp);
m = NULL;
}
drop:
(void) splx(s);
if (m)
KB_FREEALL(m);
return;
}
/*
* Process an ATMARP reply packet
*
* Arguments:
* ivp pointer to input VCC's IPVCC control block
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
static void
proc_arp_rsp(ivp, m)
struct ipvcc *ivp;
KBuffer *m;
{
struct ip_nif *inp;
struct atm_nif *nip;
struct siginst *sgp;
struct uniip *uip;
struct uniarp *uap;
struct in_addr myip;
int s = splnet();
/*
* Only the arp server should send these
*/
inp = ivp->iv_ipnif;
nip = inp->inf_nif;
uip = (struct uniip *)inp->inf_isintf;
if ((uip == NULL) ||
(uip->uip_arpsvrvcc != ivp))
goto drop;
/*
* Locate our addresses
*/
sgp = nip->nif_pif->pif_siginst;
myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr;
/*
* Target addresses must be ours
*/
if ((tip.s_addr != myip.s_addr) ||
!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) ||
!ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub))
goto drop;
/*
* Drop packet if both Source addresses aren't present
*/
if ((sip.s_addr == 0) || (satm.address_format == T_ATM_ABSENT))
goto drop;
/*
* If the Source addresses are ours, this is an arp server
* registration response
*/
if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) &&
ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) {
if (sip.s_addr == myip.s_addr) {
/*
* Registration response - update our state and
* set a registration refresh timer
*/
if (uip->uip_arpstate == UIAS_CLIENT_REGISTER)
uip->uip_arpstate = UIAS_CLIENT_ACTIVE;
if (uip->uip_arpstate == UIAS_CLIENT_ACTIVE) {
UNIIP_ARP_CANCEL(uip);
UNIIP_ARP_TIMER(uip, UNIARP_REGIS_REFRESH);
}
/*
* If the cache entry for the server VCC isn't valid
* yet, then send an Inverse ATMARP request to solicit
* the server's IP address
*/
uap = (struct uniarp *)ivp->iv_arpent;
if ((uap->ua_flags & UAF_VALID) == 0) {
(void) uniarp_inarp_req(uip, &uap->ua_dstatm,
&uap->ua_dstatmsub, ivp);
}
goto drop;
} else {
log(LOG_WARNING,
"uniarp: arpserver has our IP address wrong\n");
goto drop;
}
} else if (sip.s_addr == myip.s_addr) {
log(LOG_WARNING,
"uniarp: arpserver has our ATM address wrong\n");
goto drop;
}
/*
* Validate the Source IP address
*/
if (uniarp_validate_ip(uip, &sip, UAO_LOOKUP) != 0)
goto drop;
/*
* Now we believe this packet contains an authoritative mapping,
* which we probably need to setup an outgoing SVC connection
*/
(void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_LOOKUP);
drop:
(void) splx(s);
KB_FREEALL(m);
return;
}
/*
* Process an ATMARP negative ack packet
*
* Arguments:
* ivp pointer to input VCC's IPVCC control block
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
static void
proc_arp_nak(ivp, m)
struct ipvcc *ivp;
KBuffer *m;
{
struct ip_nif *inp;
struct atm_nif *nip;
struct siginst *sgp;
struct uniip *uip;
struct uniarp *uap;
struct in_addr myip;
struct ipvcc *inext;
int s = splnet();
/*
* Only the arp server should send these
*/
inp = ivp->iv_ipnif;
nip = inp->inf_nif;
uip = (struct uniip *)inp->inf_isintf;
if ((uip == NULL) ||
(uip->uip_arpsvrvcc != ivp))
goto drop;
/*
* Locate our addresses
*/
sgp = nip->nif_pif->pif_siginst;
myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr;
/*
* Source addresses must be ours
*/
if ((sip.s_addr != myip.s_addr) ||
!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) ||
!ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub))
goto drop;
/*
* Drop packet if the Target IP address isn't there or if this
* is a registration response, indicating an old or flakey server
*/
if ((tip.s_addr == 0) || (tip.s_addr == myip.s_addr))
goto drop;
/*
* Otherwise, see who we were looking for
*/
UNIARP_LOOKUP(tip.s_addr, uap);
if (uap == NULL)
goto drop;
/*
* This entry isn't valid any longer, so notify all VCCs using this
* entry that they must finish up. The last notify should cause
* this entry to be freed by the vcclose() function.
*/
uap->ua_flags &= ~UAF_VALID;
for (ivp = uap->ua_ivp; ivp; ivp = inext) {
inext = ivp->iv_arpnext;
(*inp->inf_arpnotify)(ivp, MAP_FAILED);
}
drop:
(void) splx(s);
KB_FREEALL(m);
return;
}
/*
* Process an InATMARP request packet
*
* Arguments:
* ivp pointer to input VCC's IPVCC control block
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
static void
proc_inarp_req(ivp, m)
struct ipvcc *ivp;
KBuffer *m;
{
struct ip_nif *inp;
struct atm_nif *nip;
struct siginst *sgp;
struct uniip *uip;
struct in_addr myip;
int s = splnet();
/*
* Get interface pointers
*/
inp = ivp->iv_ipnif;
nip = inp->inf_nif;
uip = (struct uniip *)inp->inf_isintf;
if (uip == NULL)
goto drop;
/*
* Locate our addresses
*/
sgp = nip->nif_pif->pif_siginst;
myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr;
/*
* Packet must have a Source IP address and, if it was received
* over an SVC, a Source ATM address too.
*/
if ((sip.s_addr == 0) ||
((ivp->iv_flags & IVF_SVC) && (satm.address_format == T_ATM_ABSENT)))
goto drop;
/*
* Validate Source ATM address
* - can't be me
*/
if (satm.address_format != T_ATM_ABSENT) {
if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) &&
ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel,
&satmsub))
goto drop;
}
/*
* Validate Source IP address
*/
if ((sip.s_addr == myip.s_addr) ||
(uniarp_validate_ip(uip, &sip, UAO_PEER_REQ) != 0))
goto drop;
/*
* The Target ATM address is required for a packet received over
* an SVC, optional for a PVC. If one is present, it must be our
* address.
*/
if ((ivp->iv_flags & IVF_SVC) && (tatm.address_format == T_ATM_ABSENT))
goto drop;
if ((tatm.address_format != T_ATM_ABSENT) &&
(!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) ||
!ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub)))
goto drop;
/*
* See where this packet is from
*/
if (ivp->iv_flags & IVF_PVC) {
/*
* Process the PVC arp data, although we don't really
* update the arp cache with this information
*/
uniarp_cache_pvc(ivp, &sip, &satm, &satmsub);
} else if (uip->uip_arpsvrvcc == ivp) {
/*
* Packet is from the arp server, so we've received a
* registration/refresh request (1577 version).
*
* Therefore, update cache with authoritative data.
*/
(void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_LOOKUP);
/*
* Make sure the cache update didn't kill the server VCC
*/
if (uip->uip_arpsvrvcc != ivp)
goto drop;
/*
* Update the server state and set the
* registration refresh timer
*/
uip->uip_arpstate = UIAS_CLIENT_ACTIVE;
UNIIP_ARP_CANCEL(uip);
UNIIP_ARP_TIMER(uip, UNIARP_REGIS_REFRESH);
} else {
/*
* Otherwise, we consider this source mapping data as
* non-authoritative and update the cache appropriately
*/
if (uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_PEER_REQ))
goto drop;
}
/*
* Send an InATMARP response back to originator
*/
(void) uniarp_inarp_rsp(uip, &sip, &satm, &satmsub, ivp);
drop:
(void) splx(s);
KB_FREEALL(m);
return;
}
/*
* Process an InATMARP response packet
*
* Arguments:
* ivp pointer to input VCC's IPVCC control block
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
static void
proc_inarp_rsp(ivp, m)
struct ipvcc *ivp;
KBuffer *m;
{
struct ip_nif *inp;
struct atm_nif *nip;
struct siginst *sgp;
struct uniip *uip;
struct in_addr myip;
int s = splnet();
/*
* Get interface pointers
*/
inp = ivp->iv_ipnif;
nip = inp->inf_nif;
uip = (struct uniip *)inp->inf_isintf;
if (uip == NULL)
goto drop;
/*
* Locate our addresses
*/
sgp = nip->nif_pif->pif_siginst;
myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr;
/*
* Packet must have a Source IP address and, if it was received
* over an SVC, a Source ATM address too.
*/
if ((sip.s_addr == 0) ||
((ivp->iv_flags & IVF_SVC) && (satm.address_format == T_ATM_ABSENT)))
goto drop;
/*
* Validate Source ATM address
* - can't be me
*/
if (satm.address_format != T_ATM_ABSENT) {
if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) &&
ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel,
&satmsub))
goto drop;
}
/*
* Validate Source IP address
* - must be in our LIS
* - can't be me
* - can't be broadcast
* - can't be multicast
*/
if ((sip.s_addr == myip.s_addr) ||
(uniarp_validate_ip(uip, &sip, UAO_PEER_RSP) != 0))
goto drop;
/*
* The Target ATM address is required for a packet received over
* an SVC, optional for a PVC. If one is present, it must be our
* address.
*/
if ((ivp->iv_flags & IVF_SVC) && (tatm.address_format == T_ATM_ABSENT))
goto drop;
if ((tatm.address_format != T_ATM_ABSENT) &&
(!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) ||
!ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub)))
goto drop;
/*
* See where this packet is from
*/
if (ivp->iv_flags & IVF_PVC) {
/*
* Process the PVC arp data, although we don't really
* update the arp cache with this information
*/
uniarp_cache_pvc(ivp, &sip, &satm, &satmsub);
} else {
/*
* Can't tell the difference between an RFC-1577 registration
* and a data connection from a client of another arpserver
* on our LIS (using SCSP) - so we'll update the cache now
* with what we've got. Our clients will get "registered"
* when (if) they query us with an arp request.
*/
(void) uniarp_cache_svc(uip, &sip, &satm, &satmsub,
UAO_PEER_RSP);
}
drop:
(void) splx(s);
KB_FREEALL(m);
return;
}
/*
* Print an ATMARP PDU
*
* Arguments:
* ivp pointer to input VCC control block
* m pointer to pdu buffer chain
* msg pointer to message string
*
* Returns:
* none
*
*/
void
uniarp_pdu_print(ivp, m, msg)
struct ipvcc *ivp;
KBuffer *m;
char *msg;
{
char buf[128];
struct vccb *vcp;
vcp = ivp->iv_conn->co_connvc->cvc_vcc;
snprintf(buf, sizeof(buf),
"uniarp %s: vcc=(%d,%d)\n", msg, vcp->vc_vpi, vcp->vc_vci);
atm_pdu_print(m, buf);
}