* Copyright (c) 1982, 1989 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_ethersubr.c 7.22 (Berkeley) %G%
#include <net/if_types.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#include <netiso/argo_debug.h>
#include <netiso/iso_var.h>
#include <netiso/iso_snpac.h>
u_char etherbroadcastaddr
[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
extern struct ifnet loif
;
#define senderr(e) { error = (e); goto bad;}
* Ethernet output routine.
* Encapsulate a packet of type family for the local net.
* Use trailer local net encapsulation if enough data in first
* packet leaves a multiple of 512 bytes of data in remainder.
* Assumes that ifp is actually pointer to arpcom structure.
ether_output(ifp
, m0
, dst
, rt0
)
register struct ifnet
*ifp
;
register struct mbuf
*m
= m0
;
register struct rtentry
*rt
;
struct mbuf
*mcopy
= (struct mbuf
*)0;
register struct ether_header
*eh
;
int off
, len
= m
->m_pkthdr
.len
;
struct arpcom
*ac
= (struct arpcom
*)ifp
;
if ((ifp
->if_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
))
ifp
->if_lastchange
= time
;
if ((rt
->rt_flags
& RTF_UP
) == 0) {
if (rt0
= rt
= rtalloc1(dst
, 1))
if (rt
->rt_flags
& RTF_GATEWAY
) {
if (((rt
= rt
->rt_gwroute
)->rt_flags
& RTF_UP
) == 0) {
lookup
: rt
->rt_gwroute
= rtalloc1(rt
->rt_gateway
, 1);
if ((rt
= rt
->rt_gwroute
) == 0)
if (rt
->rt_flags
& RTF_REJECT
)
if (rt
->rt_rmx
.rmx_expire
== 0 ||
time
.tv_sec
< rt
->rt_rmx
.rmx_expire
)
senderr(rt
== rt0
? EHOSTDOWN
: EHOSTUNREACH
);
switch (dst
->sa_family
) {
if (!arpresolve(ac
, rt
, m
, (struct sockaddr_in
*)dst
,
return (0); /* if not yet resolved */
/* If broadcasting on a simplex interface, loopback a copy */
if ((m
->m_flags
& M_BCAST
) && (ifp
->if_flags
& IFF_SIMPLEX
))
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
off
= m
->m_pkthdr
.len
- m
->m_len
;
bcopy((caddr_t
)&(((struct sockaddr_ns
*)dst
)->sns_addr
.x_host
),
(caddr_t
)edst
, sizeof (edst
));
if (!bcmp((caddr_t
)edst
, (caddr_t
)&ns_thishost
, sizeof(edst
)))
return (looutput(ifp
, m
, dst
, rt
));
/* If broadcasting on a simplex interface, loopback a copy */
if ((m
->m_flags
& M_BCAST
) && (ifp
->if_flags
& IFF_SIMPLEX
))
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
register struct sockaddr_dl
*sdl
;
if (rt
&& (sdl
= (struct sockaddr_dl
*)rt
->rt_gateway
) &&
sdl
->sdl_family
== AF_LINK
&& sdl
->sdl_alen
> 0) {
bcopy(LLADDR(sdl
), (caddr_t
)edst
, sizeof(edst
));
iso_snparesolve(ifp
, (struct sockaddr_iso
*)dst
,
goto bad
; /* Not Resolved */
/* If broadcasting on a simplex interface, loopback a copy */
m
->m_flags
|= (M_BCAST
|M_MCAST
);
if ((m
->m_flags
& M_BCAST
) && (ifp
->if_flags
& IFF_SIMPLEX
) &&
(mcopy
= m_copy(m
, 0, (int)M_COPYALL
))) {
M_PREPEND(mcopy
, sizeof (*eh
), M_DONTWAIT
);
eh
= mtod(mcopy
, struct ether_header
*);
(caddr_t
)eh
->ether_dhost
, sizeof (edst
));
bcopy((caddr_t
)ac
->ac_enaddr
,
(caddr_t
)eh
->ether_shost
, sizeof (edst
));
M_PREPEND(m
, 3, M_DONTWAIT
);
l
= mtod(m
, struct llc
*);
l
->llc_dsap
= l
->llc_ssap
= LLC_ISO_LSAP
;
printf("unoutput: sending pkt to: ");
printf("%x ", edst
[i
] & 0xff);
eh
= (struct ether_header
*)dst
->sa_data
;
bcopy((caddr_t
)eh
->ether_dhost
, (caddr_t
)edst
, sizeof (edst
));
printf("%s%d: can't handle af%d\n", ifp
->if_name
, ifp
->if_unit
,
(void) looutput(ifp
, mcopy
, dst
, rt
);
* Add local net header. If no space in first mbuf,
M_PREPEND(m
, sizeof (struct ether_header
), M_DONTWAIT
);
eh
= mtod(m
, struct ether_header
*);
type
= htons((u_short
)type
);
bcopy((caddr_t
)&type
,(caddr_t
)&eh
->ether_type
,
bcopy((caddr_t
)edst
, (caddr_t
)eh
->ether_dhost
, sizeof (edst
));
bcopy((caddr_t
)ac
->ac_enaddr
, (caddr_t
)eh
->ether_shost
,
sizeof(eh
->ether_shost
));
* Queue message on interface, and start output if interface
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
if ((ifp
->if_flags
& IFF_OACTIVE
) == 0)
ifp
->if_obytes
+= len
+ sizeof (struct ether_header
);
if (m
->m_flags
& M_MCAST
)
* Process a received Ethernet packet;
* the packet is in the mbuf chain m without
* the ether header, which is provided separately.
register struct ether_header
*eh
;
register struct ifqueue
*inq
;
if ((ifp
->if_flags
& IFF_UP
) == 0) {
ifp
->if_lastchange
= time
;
ifp
->if_ibytes
+= m
->m_pkthdr
.len
+ sizeof (*eh
);
if (bcmp((caddr_t
)etherbroadcastaddr
, (caddr_t
)eh
->ether_dhost
,
sizeof(etherbroadcastaddr
)) == 0)
else if (eh
->ether_dhost
[0] & 1)
if (m
->m_flags
& (M_BCAST
|M_MCAST
))
switch (eh
->ether_type
) {
if (eh
->ether_type
> ETHERMTU
)
l
= mtod(m
, struct llc
*);
switch (l
->llc_control
) {
/* LLC_UI_P forbidden in class 1 service */
if ((l
->llc_dsap
== LLC_ISO_LSAP
) &&
(l
->llc_ssap
== LLC_ISO_LSAP
)) {
if (m
->m_pkthdr
.len
> eh
->ether_type
)
m_adj(m
, eh
->ether_type
- m
->m_pkthdr
.len
);
m
->m_data
+= 3; /* XXX */
m
->m_pkthdr
.len
-= 3; /* XXX */
M_PREPEND(m
, sizeof *eh
, M_DONTWAIT
);
*mtod(m
, struct ether_header
*) = *eh
;
l
->llc_dsap
= l
->llc_ssap
= 0;
register struct ether_header
*eh2
;
l
->llc_dsap
= l
->llc_ssap
;
if (m
->m_flags
& (M_BCAST
| M_MCAST
))
bcopy((caddr_t
)((struct arpcom
*)ifp
)->ac_enaddr
,
(caddr_t
)eh
->ether_dhost
, 6);
sa
.sa_family
= AF_UNSPEC
;
eh2
= (struct ether_header
*)sa
.sa_data
;
for (i
= 0; i
< 6; i
++) {
eh2
->ether_shost
[i
] = c
= eh
->ether_dhost
[i
];
eh
->ether_dhost
[i
] = eh
->ether_shost
[i
];
ifp
->if_output(ifp
, m
, &sa
, (struct rtentry
*)0);
* Convert Ethernet address to printable (loggable) representation.
static char digits
[] = "0123456789abcdef";
static char etherbuf
[18];
register char *cp
= etherbuf
;
for (i
= 0; i
< 6; i
++) {
*cp
++ = digits
[*ap
>> 4];
*cp
++ = digits
[*ap
++ & 0xf];
* Perform common duties while attaching to interface list
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
;
register struct sockaddr_dl
*sdl
;
ifp
->if_type
= IFT_ETHER
;
for (ifa
= ifp
->if_addrlist
; ifa
; ifa
= ifa
->ifa_next
)
if ((sdl
= (struct sockaddr_dl
*)ifa
->ifa_addr
) &&
sdl
->sdl_family
== AF_LINK
) {
sdl
->sdl_type
= IFT_ETHER
;
sdl
->sdl_alen
= ifp
->if_addrlen
;
bcopy((caddr_t
)((struct arpcom
*)ifp
)->ac_enaddr
,
LLADDR(sdl
), ifp
->if_addrlen
);
u_char ether_ipmulticast_min
[6] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
u_char ether_ipmulticast_max
[6] = { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
* Add an Ethernet multicast address or range of addresses to the list for a
register struct arpcom
*ac
;
register struct ether_multi
*enm
;
switch (ifr
->ifr_addr
.sa_family
) {
bcopy(ifr
->ifr_addr
.sa_data
, addrlo
, 6);
bcopy(addrlo
, addrhi
, 6);
sin
= (struct sockaddr_in
*)&(ifr
->ifr_addr
);
if (sin
->sin_addr
.s_addr
== INADDR_ANY
) {
* An IP address of INADDR_ANY means listen to all
* of the Ethernet multicast addresses used for IP.
* (This is for the sake of IP multicast routers.)
bcopy(ether_ipmulticast_min
, addrlo
, 6);
bcopy(ether_ipmulticast_max
, addrhi
, 6);
ETHER_MAP_IP_MULTICAST(&sin
->sin_addr
, addrlo
);
bcopy(addrlo
, addrhi
, 6);
* Verify that we have valid Ethernet multicast addresses.
if ((addrlo
[0] & 0x01) != 1 || (addrhi
[0] & 0x01) != 1) {
* See if the address range is already in the list.
ETHER_LOOKUP_MULTI(addrlo
, addrhi
, ac
, enm
);
* Found it; just increment the reference count.
* New address or range; malloc a new multicast record
* and link it into the interface's multicast list.
enm
= (struct ether_multi
*)malloc(sizeof(*enm
), M_IFMADDR
, M_NOWAIT
);
bcopy(addrlo
, enm
->enm_addrlo
, 6);
bcopy(addrhi
, enm
->enm_addrhi
, 6);
enm
->enm_next
= ac
->ac_multiaddrs
;
* Return ENETRESET to inform the driver that the list has changed
* and its reception filter should be adjusted accordingly.
* Delete a multicast address record.
register struct arpcom
*ac
;
register struct ether_multi
*enm
;
register struct ether_multi
**p
;
switch (ifr
->ifr_addr
.sa_family
) {
bcopy(ifr
->ifr_addr
.sa_data
, addrlo
, 6);
bcopy(addrlo
, addrhi
, 6);
sin
= (struct sockaddr_in
*)&(ifr
->ifr_addr
);
if (sin
->sin_addr
.s_addr
== INADDR_ANY
) {
* An IP address of INADDR_ANY means stop listening
* to the range of Ethernet multicast addresses used
bcopy(ether_ipmulticast_min
, addrlo
, 6);
bcopy(ether_ipmulticast_max
, addrhi
, 6);
ETHER_MAP_IP_MULTICAST(&sin
->sin_addr
, addrlo
);
bcopy(addrlo
, addrhi
, 6);
* Look up the address in our list.
ETHER_LOOKUP_MULTI(addrlo
, addrhi
, ac
, enm
);
if (--enm
->enm_refcount
!= 0) {
* Still some claims to this record.
* No remaining claims to this record; unlink and free it.
for (p
= &enm
->enm_ac
->ac_multiaddrs
;
* Return ENETRESET to inform the driver that the list has changed
* and its reception filter should be adjusted accordingly.