Compare commits

..

9 Commits

Author SHA1 Message Date
fd484754e5 [ci] Cache downloaded packages for GitHub actions
Speed up the "Install packages" step for each CI run by caching the
downloaded packages in /var/cache/apt.

Do not include libc6-dbg:i386 within the cache, since apt seems to
complain if asked to download both gcc-aarch64-linux-gnu and
libc6-dbg:i386 at the same time.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-06 18:47:28 +00:00
ef0a6f4792 [ioapi] Move PAGE_SHIFT to bits/io.h
The PAGE_SHIFT definition is an architectural property, rather than an
aspect of a particular I/O API implementation (of which, in theory,
there may be more than one per architecture).

Reflect this by moving the definition to the top-level bits/io.h for
each architecture.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-06 12:34:21 +00:00
c6901792f0 [build] Allow for per-architecture unprefixed constant operand modifier
Over the years, the undocumented operand modifier used to produce the
unprefixed constant values in __einfo_error() has varied from "%c0" to
"%a0" in commit 1a77466 ("[build] Fix use of inline assembly on GCC
4.8 ARM64 builds") and back to "%c0" in commit 3fb3ffc ("[build] Fix
use of inline assembly on GCC 8 ARM64 builds"), according to the
evolving demands of the toolchain.

LoongArch64 suffers from a similar issue: GCC 13 will allow either,
but the currently released GCC 12 allows only the "%a0" form.

Introduce a macro ASM_NO_PREFIX, defined in bits/compiler.h, to
abstract away this difference and allow different architectures to use
different operand modifiers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-05 23:55:14 +00:00
a2bed43939 [xen] Allow for platforms that have no Xen support
The Xen headers support only x86 and ARM.  Allow for platforms such as
LoongArch64 to build despite the absence of Xen support by providing
an architecture-specific <bits/xen.h> that simply does:

  #ifndef _BITS_XEN_H
  #define _BITS_XEN_H
  #include <ipxe/nonxen.h>
  #endif /* _BITS_XEN_H */

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-05 22:21:36 +00:00
7cc305f7b4 [efi] Enable NET_PROTO_LLDP by default
Requested-by: Christian I. Nilsson <nikize@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-05 18:54:39 +00:00
dc16de3204 [lldp] Add support for the Link Layer Discovery Protocol
Add support for recording LLDP packets and exposing TLV values via the
settings mechanism.  LLDP settings are encoded as

  ${netX.lldp/<prefix>.<type>.<index>.<offset>.<length>}

where

  <type> is the TLV type

  <offset> is the starting offset within the TLV value

  <length> is the length (or zero to read the from <offset> to the end)

  <prefix>, if it has a non-zero value, is the subtype byte string of
  length <offset> to match at the start of the TLV value, up to a
  maximum matched length of 4 bytes

  <index> is the index of the entry matching <type> and <prefix> to be
  accessed, with zero indicating the first matching entry

The <prefix> is designed to accommodate both matching of the OUI
within an organization-specific TLV (e.g. 0x0080c2 for IEEE 802.1
TLVs) and of a subtype byte as found within many TLVs.

This encoding allows most LLDP values to be extracted easily.  For
example

  System name: ${netX.lldp/5.0.0.0:string}

  System description: ${netX.lldp/6.0.0.0:string}

  Port description: ${netX.lldp/4.0.0.0:string}

  Port interface name: ${netX.lldp/5.2.0.1.0:string}

  Chassis MAC address: ${netX.lldp/4.1.0.1.0:hex}

  Management IPv4 address: ${netX.lldp/5.1.8.0.2.4:ipv4}

  Port VLAN ID: ${netX.lldp/0x0080c2.1.127.0.4.2:int16}

  Port VLAN name: ${netX.lldp/0x0080c2.3.127.0.7.0:string}

  Maximum frame size: ${netX.lldp/0x00120f.4.127.0.4.2:uint16}

Originally-implemented-by: Marin Hannache <git@mareo.fr>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-05 18:18:02 +00:00
6c0335adf6 [ci] Update to ubuntu-22.04 GitHub actions runner
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-03 20:08:16 +00:00
8450fa4a7b [dhcp] Ignore DHCPNAK unless originating from the selected DHCP server
RFC 2131 leaves undefined the behaviour of the client in response to a
DHCPNAK that comes from a server other than the selected DHCP server.

A substantial amount of online documentation suggests using multiple
independent DHCP servers with non-overlapping ranges in the same
subnet in order to provide some minimal redundancy.  Experimentation
shows that in this setup, at least ISC dhcpd will send a DHCPNAK in
response to the client's DHCPREQUEST for an address that is not within
the range defined on that server.  (Since the requested address does
lie within the subnet defined on that server, this will happen
regardless of the "authoritative" parameter.)  The client will
therefore receive a DHCPACK from the selected DHCP server along with
one or more DHCPNAKs from each of the non-selected DHCP servers.

Filter out responses from non-selected DHCP servers before checking
for a DHCPNAK, so that these arguably spurious DHCPNAKs will not cause
iPXE to return to the discovery state.

Continue to check for DHCPNAK before filtering out responses for
non-selected lease addresses, since experimentation shows that the
DHCPNAK will usually have an empty yiaddr field.

Reported-by: Anders Blomdell <anders.blomdell@control.lth.se>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-03 19:51:58 +00:00
4e456d9928 [efi] Do not attempt to drive PCI bridge devices
The "bridge" driver introduced in 3aa6b79 ("[pci] Add minimal PCI
bridge driver") is required only for BIOS builds using the ENA driver,
where experimentation shows that we cannot rely on the BIOS to fully
assign MMIO addresses.

Since the driver is a valid PCI driver, it will end up binding to all
PCI bridge devices even on a UEFI platform, where the firmware is
likely to have completed MMIO address assignment correctly.  This has
no impact on most systems since there is generally no UEFI driver for
PCI bridges: the enumeration of the whole PCI bus is handled by the
PciBusDxe driver bound to the root bridge.

Experimentation shows that at least one laptop will freeze at the
point that iPXE attempts to bind to the bridge device.  No deeper
investigation has been carried out to find the root cause.

Fix by causing efipci_supported() to return an error unless the
configuration space header type indicates a non-bridge device.

Reported-by: Marcel Petersen <mp@sbe.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-02-03 16:10:31 +00:00
25 changed files with 601 additions and 15 deletions

View File

@ -4,14 +4,37 @@ on: push
jobs:
cache:
name: Cache
runs-on: ubuntu-22.04
steps:
- name: Cache packages
uses: actions/cache@v3
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache
- name: Download packages
run: |
sudo apt update
sudo apt install -y -d -o Acquire::Retries=50 \
mtools syslinux isolinux \
libc6-dev-i386 valgrind \
gcc-arm-none-eabi gcc-aarch64-linux-gnu
x86:
name: x86
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs: cache
steps:
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cached packages
uses: actions/cache/restore@v3
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache
- name: Install packages
run: |
sudo dpkg --add-architecture i386
@ -32,12 +55,18 @@ jobs:
arm32:
name: ARM32
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs: cache
steps:
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cached packages
uses: actions/cache/restore@v3
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache
- name: Install packages
run: |
sudo apt update
@ -52,12 +81,18 @@ jobs:
arm64:
name: ARM64
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs: cache
steps:
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cached packages
uses: actions/cache/restore@v3
with:
path: /var/cache/apt/archives/*.deb
key: apt-cache
- name: Install packages
run: |
sudo apt update

View File

@ -8,7 +8,7 @@ on:
jobs:
submit:
name: Submit
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3

View File

@ -9,6 +9,9 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Page shift */
#define PAGE_SHIFT 12
#include <ipxe/arm_io.h>
#endif /* _BITS_IO_H */

View File

@ -20,9 +20,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
/** Page shift */
#define PAGE_SHIFT 12
/*
* Physical<->Bus address mappings
*

View File

@ -8,6 +8,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef ASSEMBLY
/** Unprefixed constant operand modifier */
#define ASM_NO_PREFIX "c"
#define __asmcall
#define __libgcc

View File

@ -8,6 +8,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef ASSEMBLY
/** Unprefixed constant operand modifier */
#define ASM_NO_PREFIX "c"
#define __asmcall
#define __libgcc

View File

@ -8,6 +8,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef ASSEMBLY
/** Unprefixed constant operand modifier */
#define ASM_NO_PREFIX "c"
/** Declare a function with standard calling conventions */
#define __asmcall __attribute__ (( cdecl, regparm(0) ))

View File

@ -9,6 +9,9 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Page shift */
#define PAGE_SHIFT 12
#include <ipxe/x86_io.h>
#endif /* _BITS_IO_H */

View File

@ -28,9 +28,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
/** Page shift */
#define PAGE_SHIFT 12
/*
* Physical<->Bus address mappings
*

View File

@ -8,6 +8,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifndef ASSEMBLY
/** Unprefixed constant operand modifier */
#define ASM_NO_PREFIX "c"
/** Declare a function with standard calling conventions */
#define __asmcall __attribute__ (( regparm(0) ))

View File

@ -49,3 +49,6 @@ REQUIRE_OBJECT ( eth_slow );
#ifdef NET_PROTO_EAPOL
REQUIRE_OBJECT ( eapol );
#endif
#ifdef NET_PROTO_LLDP
REQUIRE_OBJECT ( lldp );
#endif

View File

@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define FDT_EFI
#define NET_PROTO_IPV6 /* IPv6 protocol */
#define NET_PROTO_LLDP /* Link Layer Discovery protocol */
#define DOWNLOAD_PROTO_FILE /* Local filesystem access */

View File

@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define NET_PROTO_STP /* Spanning Tree protocol */
#define NET_PROTO_LACP /* Link Aggregation control protocol */
#define NET_PROTO_EAPOL /* EAP over LAN protocol */
//#define NET_PROTO_LLDP /* Link Layer Discovery protocol */
/*
* PXE support

View File

@ -205,6 +205,7 @@ int pci_read_config ( struct pci_device *pci ) {
pci_read_config_dword ( pci, PCI_REVISION, &tmp );
pci->class = ( tmp >> 8 );
pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, &pci->irq );
pci_read_config_byte ( pci, PCI_HEADER_TYPE, &pci->hdrtype );
pci_read_bases ( pci );
/* Initialise generic device component */

View File

@ -262,10 +262,10 @@ static inline void eplatform_discard ( int dummy __unused, ... ) {}
".balign 8\n\t" \
"\n1:\n\t" \
".long ( 4f - 1b )\n\t" \
".long %c0\n\t" \
".long %" ASM_NO_PREFIX "0\n\t" \
".long ( 2f - 1b )\n\t" \
".long ( 3f - 1b )\n\t" \
".long %c1\n\t" \
".long %" ASM_NO_PREFIX "1\n\t" \
"\n2:\t.asciz \"" __einfo_desc ( einfo ) "\"\n\t" \
"\n3:\t.asciz \"" __FILE__ "\"\n\t" \
".balign 8\n\t" \

View File

@ -295,6 +295,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 )
#define ERRFILE_httpntlm ( ERRFILE_NET | 0x004a0000 )
#define ERRFILE_eap ( ERRFILE_NET | 0x004b0000 )
#define ERRFILE_lldp ( ERRFILE_NET | 0x004c0000 )
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )

View File

@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ETH_P_SLOW 0x8809 /* Ethernet slow protocols */
#define ETH_P_EAPOL 0x888E /* 802.1X EAP over LANs */
#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */

97
src/include/ipxe/lldp.h Normal file
View File

@ -0,0 +1,97 @@
#ifndef _IPXE_LLDP_H
#define _IPXE_LLDP_H
/** @file
*
* Link Layer Discovery Protocol
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/** An LLDP TLV header */
struct lldp_tlv {
/** Type and length */
uint16_t type_len;
/** Data */
uint8_t data[0];
} __attribute__ (( packed ));
/**
* Extract LLDP TLV type
*
* @v type_len Type and length
* @ret type Type
*/
#define LLDP_TLV_TYPE( type_len ) ( (type_len) >> 9 )
/**
* Extract LLDP TLV length
*
* @v type_len Type and length
* @ret len Length
*/
#define LLDP_TLV_LEN( type_len ) ( (type_len) & 0x01ff )
/** End of LLDP data unit */
#define LLDP_TYPE_END 0x00
/** LLDP settings block name */
#define LLDP_SETTINGS_NAME "lldp"
/**
* Construct LLDP setting tag
*
* LLDP settings are encoded as
*
* ${netX.lldp/<prefix>.<type>.<index>.<offset>.<length>}
*
* where
*
* <type> is the TLV type
*
* <offset> is the starting offset within the TLV value
*
* <length> is the length (or zero to read the from <offset> to the end)
*
* <prefix>, if it has a non-zero value, is the subtype byte string
* of length <offset> to match at the start of the TLV value, up to
* a maximum matched length of 4 bytes
*
* <index> is the index of the entry matching <type> and <prefix> to
* be accessed, with zero indicating the first matching entry
*
* The <prefix> is designed to accommodate both matching of the OUI
* within an organization-specific TLV (e.g. 0x0080c2 for IEEE 802.1
* TLVs) and of a subtype byte as found within many TLVs.
*
* This encoding allows most LLDP values to be extracted easily. For
* example
*
* System name: ${netX.lldp/5.0.0.0:string}
*
* System description: ${netX.lldp/6.0.0.0:string}
*
* Port description: ${netX.lldp/4.0.0.0:string}
*
* Port interface name: ${netX.lldp/5.2.0.1.0:string}
*
* Chassis MAC address: ${netX.lldp/4.1.0.1.0:hex}
*
* Management IPv4 address: ${netX.lldp/5.1.8.0.2.4:ipv4}
*
* Port VLAN ID: ${netX.lldp/0x0080c2.1.127.0.4.2:int16}
*
* Port VLAN name: ${netX.lldp/0x0080c2.3.127.0.7.0:string}
*
* Maximum frame size: ${netX.lldp/0x00120f.4.127.0.4.2:uint16}
*
*/
#define LLDP_TAG( prefix, type, index, offset, length ) \
( ( ( ( uint64_t ) (prefix) ) << 32 ) | \
( (type) << 24 ) | ( (index) << 16 ) | \
( (offset) << 8 ) | ( (length) << 0 ) )
#endif /* _IPXE_LLDP_H */

76
src/include/ipxe/nonxen.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef _IPXE_NONXEN_H
#define _IPXE_NONXEN_H
/** @file
*
* Stub Xen definitions for platforms with no Xen support
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define __XEN_GUEST_HANDLE(name) __guest_handle_ ## name
#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name)
#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \
typedef type * __XEN_GUEST_HANDLE(name)
#define __DEFINE_XEN_GUEST_HANDLE(name, type) \
___DEFINE_XEN_GUEST_HANDLE(name, type); \
___DEFINE_XEN_GUEST_HANDLE(const_##name, const type)
#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name)
typedef unsigned long xen_pfn_t;
typedef unsigned long xen_ulong_t;
struct arch_vcpu_info {};
struct arch_shared_info {};
#define XEN_LEGACY_MAX_VCPUS 0
struct xen_hypervisor;
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_1 ( struct xen_hypervisor *xen __unused,
unsigned int hypercall __unused,
unsigned long arg1 __unused ) {
return 1;
}
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_2 ( struct xen_hypervisor *xen __unused,
unsigned int hypercall __unused,
unsigned long arg1 __unused, unsigned long arg2 __unused ) {
return 1;
}
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_3 ( struct xen_hypervisor *xen __unused,
unsigned int hypercall __unused,
unsigned long arg1 __unused, unsigned long arg2 __unused,
unsigned long arg3 __unused ) {
return 1;
}
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_4 ( struct xen_hypervisor *xen __unused,
unsigned int hypercall __unused,
unsigned long arg1 __unused, unsigned long arg2 __unused,
unsigned long arg3 __unused, unsigned long arg4 __unused ) {
return 1;
}
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_5 ( struct xen_hypervisor *xen __unused,
unsigned int hypercall __unused,
unsigned long arg1 __unused, unsigned long arg2 __unused,
unsigned long arg3 __unused, unsigned long arg4 __unused,
unsigned long arg5 __unused ) {
return 1;
}
#endif /* _IPXE_NONXEN_H */

View File

@ -227,6 +227,8 @@ struct pci_device {
uint32_t class;
/** Interrupt number */
uint8_t irq;
/** Header type */
uint8_t hdrtype;
/** Segment, bus, device, and function (bus:dev.fn) number */
uint32_t busdevfn;
/** Driver for this device */

View File

@ -59,6 +59,10 @@ sub try_import_file {
if ( /^\#include\s+[<\"](\S+)[>\"]/ ) {
push @dependencies, catfile ( $subdir, $1 );
}
# Patch "Unsupported architecture" line
if ( /^\#error\s+"Unsupported\sarchitecture"/ ) {
$_ = "#include <bits/xen.h>"
}
# Write out line
print $outfh "$_\n";
# Apply FILE_LICENCE() immediately after include guard

View File

@ -19,7 +19,7 @@ FILE_LICENCE ( MIT );
#elif defined(__arm__) || defined (__aarch64__)
#include "arch-arm.h"
#else
#error "Unsupported architecture"
#include <bits/xen.h>
#endif
#ifndef __ASSEMBLY__

View File

@ -785,12 +785,22 @@ int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) {
*/
static int efipci_supported ( EFI_HANDLE device ) {
struct efi_pci_device efipci;
uint8_t hdrtype;
int rc;
/* Get PCI device information */
if ( ( rc = efipci_info ( device, &efipci ) ) != 0 )
return rc;
/* Do not attempt to drive bridges */
hdrtype = efipci.pci.hdrtype;
if ( ( hdrtype & PCI_HEADER_TYPE_MASK ) != PCI_HEADER_TYPE_NORMAL ) {
DBGC ( device, "EFIPCI " PCI_FMT " type %02x is not type %02x\n",
PCI_ARGS ( &efipci.pci ), hdrtype,
PCI_HEADER_TYPE_NORMAL );
return -ENOTTY;
}
/* Look for a driver */
if ( ( rc = pci_find_driver ( &efipci.pci ) ) != 0 ) {
DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) "

340
src/net/lldp.c Normal file
View File

@ -0,0 +1,340 @@
/*
* Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Link Layer Discovery Protocol
*
*/
#include <stdlib.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/if_ether.h>
#include <ipxe/settings.h>
#include <ipxe/lldp.h>
/** An LLDP settings block */
struct lldp_settings {
/** Reference counter */
struct refcnt refcnt;
/** Settings interface */
struct settings settings;
/** List of LLDP settings blocks */
struct list_head list;
/** Name */
const char *name;
/** LLDP data */
void *data;
/** Length of LLDP data */
size_t len;
};
/** LLDP settings scope */
static const struct settings_scope lldp_settings_scope;
/** List of LLDP settings blocks */
static LIST_HEAD ( lldp_settings );
/**
* Free LLDP settings block
*
* @v refcnt Reference counter
*/
static void lldp_free ( struct refcnt *refcnt ) {
struct lldp_settings *lldpset =
container_of ( refcnt, struct lldp_settings, refcnt );
DBGC ( lldpset, "LLDP %s freed\n", lldpset->name );
list_del ( &lldpset->list );
free ( lldpset->data );
free ( lldpset );
}
/**
* Find LLDP settings block
*
* @v netdev Network device
* @ret lldpset LLDP settings block
*/
static struct lldp_settings * lldp_find ( struct net_device *netdev ) {
struct lldp_settings *lldpset;
/* Find matching LLDP settings block */
list_for_each_entry ( lldpset, &lldp_settings, list ) {
if ( netdev_settings ( netdev ) == lldpset->settings.parent )
return lldpset;
}
return NULL;
}
/**
* Check applicability of LLDP setting
*
* @v settings Settings block
* @v setting Setting to fetch
* @ret applies Setting applies within this settings block
*/
static int lldp_applies ( struct settings *settings __unused,
const struct setting *setting ) {
return ( setting->scope == &lldp_settings_scope );
}
/**
* Fetch value of LLDP setting
*
* @v settings Settings block
* @v setting Setting to fetch
* @v buf Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int lldp_fetch ( struct settings *settings,
struct setting *setting,
void *buf, size_t len ) {
struct lldp_settings *lldpset =
container_of ( settings, struct lldp_settings, settings );
union {
uint32_t high;
uint8_t raw[4];
} tag_prefix;
uint32_t tag_low;
uint8_t tag_type;
uint8_t tag_index;
uint8_t tag_offset;
uint8_t tag_length;
const void *match;
const void *data;
size_t match_len;
size_t remaining;
const struct lldp_tlv *tlv;
unsigned int tlv_type_len;
unsigned int tlv_type;
unsigned int tlv_len;
/* Parse setting tag */
tag_prefix.high = htonl ( setting->tag >> 32 );
tag_low = setting->tag;
tag_type = ( tag_low >> 24 );
tag_index = ( tag_low >> 16 );
tag_offset = ( tag_low >> 8 );
tag_length = ( tag_low >> 0 );
/* Identify match prefix */
match_len = tag_offset;
if ( match_len > sizeof ( tag_prefix ) )
match_len = sizeof ( tag_prefix );
if ( ! tag_prefix.high )
match_len = 0;
match = &tag_prefix.raw[ sizeof ( tag_prefix ) - match_len ];
/* Locate matching TLV */
for ( data = lldpset->data, remaining = lldpset->len ; remaining ;
data += tlv_len, remaining -= tlv_len ) {
/* Parse TLV header */
if ( remaining < sizeof ( *tlv ) ) {
DBGC ( lldpset, "LLDP %s underlength TLV header\n",
lldpset->name );
DBGC_HDA ( lldpset, 0, data, remaining );
break;
}
tlv = data;
data += sizeof ( *tlv );
remaining -= sizeof ( *tlv );
tlv_type_len = ntohs ( tlv->type_len );
tlv_type = LLDP_TLV_TYPE ( tlv_type_len );
if ( tlv_type == LLDP_TYPE_END )
break;
tlv_len = LLDP_TLV_LEN ( tlv_type_len );
if ( remaining < tlv_len ) {
DBGC ( lldpset, "LLDP %s underlength TLV value\n",
lldpset->name );
DBGC_HDA ( lldpset, 0, data, remaining );
break;
}
DBGC2 ( lldpset, "LLDP %s found type %d:\n",
lldpset->name, tlv_type );
DBGC2_HDA ( lldpset, 0, data, tlv_len );
/* Check for matching tag type */
if ( tlv_type != tag_type )
continue;
/* Check for matching prefix */
if ( tlv_len < match_len )
continue;
if ( memcmp ( data, match, match_len ) != 0 )
continue;
/* Check for matching index */
if ( tag_index-- )
continue;
/* Skip offset */
if ( tlv_len < tag_offset )
return 0;
data += tag_offset;
tlv_len -= tag_offset;
/* Set type if not already specified */
if ( ! setting->type ) {
setting->type = ( tag_length ? &setting_type_hex :
&setting_type_string );
}
/* Extract value */
if ( tag_length && ( tlv_len > tag_length ) )
tlv_len = tag_length;
if ( len > tlv_len )
len = tlv_len;
memcpy ( buf, data, len );
return tlv_len;
}
return -ENOENT;
}
/** LLDP settings operations */
static struct settings_operations lldp_settings_operations = {
.applies = lldp_applies,
.fetch = lldp_fetch,
};
/**
* Process LLDP packet
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v ll_dest Link-layer destination address
* @v ll_source Link-layer source address
* @v flags Packet flags
* @ret rc Return status code
*/
static int lldp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
const void *ll_dest, const void *ll_source,
unsigned int flags __unused ) {
struct lldp_settings *lldpset;
size_t len;
void *data;
int rc;
/* Find matching LLDP settings block */
lldpset = lldp_find ( netdev );
if ( ! lldpset ) {
DBGC ( netdev, "LLDP %s has no \"%s\" settings block\n",
netdev->name, LLDP_SETTINGS_NAME );
rc = -ENOENT;
goto err_find;
}
/* Create trimmed copy of received LLDP data */
len = iob_len ( iobuf );
data = malloc ( len );
if ( ! data ) {
rc = -ENOMEM;
goto err_alloc;
}
memcpy ( data, iobuf->data, len );
/* Free any existing LLDP data */
free ( lldpset->data );
/* Transfer data to LLDP settings block */
lldpset->data = data;
lldpset->len = len;
data = NULL;
DBGC2 ( lldpset, "LLDP %s src %s ",
lldpset->name, netdev->ll_protocol->ntoa ( ll_source ) );
DBGC2 ( lldpset, "dst %s\n", netdev->ll_protocol->ntoa ( ll_dest ) );
DBGC2_HDA ( lldpset, 0, lldpset->data, lldpset->len );
/* Success */
rc = 0;
free ( data );
err_alloc:
err_find:
free_iob ( iobuf );
return rc;
}
/** LLDP protocol */
struct net_protocol lldp_protocol __net_protocol = {
.name = "LLDP",
.net_proto = htons ( ETH_P_LLDP ),
.rx = lldp_rx,
};
/**
* Create LLDP settings block
*
* @v netdev Network device
* @ret rc Return status code
*/
static int lldp_probe ( struct net_device *netdev ) {
struct lldp_settings *lldpset;
int rc;
/* Allocate LLDP settings block */
lldpset = zalloc ( sizeof ( *lldpset ) );
if ( ! lldpset ) {
rc = -ENOMEM;
goto err_alloc;
}
ref_init ( &lldpset->refcnt, lldp_free );
settings_init ( &lldpset->settings, &lldp_settings_operations,
&lldpset->refcnt, &lldp_settings_scope );
list_add_tail ( &lldpset->list, &lldp_settings );
lldpset->name = netdev->name;
/* Register settings */
if ( ( rc = register_settings ( &lldpset->settings, netdev_settings ( netdev ),
LLDP_SETTINGS_NAME ) ) != 0 ) {
DBGC ( lldpset, "LLDP %s could not register settings: %s\n",
lldpset->name, strerror ( rc ) );
goto err_register;
}
DBGC ( lldpset, "LLDP %s registered\n", lldpset->name );
ref_put ( &lldpset->refcnt );
return 0;
unregister_settings ( &lldpset->settings );
err_register:
ref_put ( &lldpset->refcnt );
err_alloc:
return rc;
}
/** LLDP driver */
struct net_driver lldp_driver __net_driver = {
.name = "LLDP",
.probe = lldp_probe,
};

View File

@ -571,6 +571,10 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
if ( peer->sin_port != htons ( BOOTPS_PORT ) )
return;
/* Filter out non-selected servers */
if ( server_id.s_addr != dhcp->server.s_addr )
return;
/* Handle DHCPNAK */
if ( msgtype == DHCPNAK ) {
dhcp_defer ( dhcp );
@ -580,8 +584,6 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
/* Filter out unacceptable responses */
if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
return;
if ( server_id.s_addr != dhcp->server.s_addr )
return;
if ( ip.s_addr != dhcp->offer.s_addr )
return;