mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
* refs/heads/tmp-d2d05bc: Linux 4.14.190 ath9k: Fix regression with Atheros 9271 ath9k: Fix general protection fault in ath9k_hif_usb_rx_cb parisc: Add atomic64_set_release() define to avoid CPU soft lockups io-mapping: indicate mapping failure mm/memcg: fix refcount error while moving and swapping Makefile: Fix GCC_TOOLCHAIN_DIR prefix for Clang cross compilation vt: Reject zero-sized screen buffer size. fbdev: Detect integer underflow at "struct fbcon_ops"->clear_margins. serial: 8250_mtk: Fix high-speed baud rates clamping serial: 8250: fix null-ptr-deref in serial8250_start_tx() staging: comedi: addi_apci_1564: check INSN_CONFIG_DIGITAL_TRIG shift staging: comedi: addi_apci_1500: check INSN_CONFIG_DIGITAL_TRIG shift staging: comedi: ni_6527: fix INSN_CONFIG_DIGITAL_TRIG support staging: comedi: addi_apci_1032: check INSN_CONFIG_DIGITAL_TRIG shift staging: wlan-ng: properly check endpoint types Revert "cifs: Fix the target file was deleted when rename failed." usb: xhci: Fix ASM2142/ASM3142 DMA addressing usb: xhci-mtk: fix the failure of bandwidth allocation binder: Don't use mmput() from shrinker function. x86: math-emu: Fix up 'cmp' insn for clang ias arm64: Use test_tsk_thread_flag() for checking TIF_SINGLESTEP usb: gadget: udc: gr_udc: fix memleak on error handling path in gr_ep_init() Input: synaptics - enable InterTouch for ThinkPad X1E 1st gen dmaengine: ioat setting ioat timeout as module parameter hwmon: (aspeed-pwm-tacho) Avoid possible buffer overflow regmap: dev_get_regmap_match(): fix string comparison spi: mediatek: use correct SPI_CFG2_REG MACRO Input: add `SW_MACHINE_COVER` dmaengine: tegra210-adma: Fix runtime PM imbalance on error HID: apple: Disable Fn-key key-re-mapping on clone keyboards HID: i2c-hid: add Mediacom FlexBook edge13 to descriptor override scripts/decode_stacktrace: strip basepath from all paths serial: exar: Fix GPIO configuration for Sealevel cards based on XR17V35X bonding: check return value of register_netdevice() in bond_newlink() i2c: rcar: always clear ICSAR to avoid side effects ipvs: fix the connection sync failed in some cases mlxsw: destroy workqueue when trap_register in mlxsw_emad_init bonding: check error value of register_netdevice() immediately net: smc91x: Fix possible memory leak in smc_drv_probe() drm: sun4i: hdmi: Fix inverted HPD result net: dp83640: fix SIOCSHWTSTAMP to update the struct with actual configuration ax88172a: fix ax88172a_unbind() failures hippi: Fix a size used in a 'pci_free_consistent()' in an error handling path bnxt_en: Fix race when modifying pause settings. btrfs: fix page leaks after failure to lock page for delalloc btrfs: fix mount failure caused by race with umount btrfs: fix double free on ulist after backref resolution failure ASoC: rt5670: Correct RT5670_LDO_SEL_MASK ALSA: info: Drop WARN_ON() from buffer NULL sanity check uprobes: Change handle_swbp() to send SIGTRAP with si_code=SI_KERNEL, to fix GDB regression IB/umem: fix reference count leak in ib_umem_odp_get() spi: spi-fsl-dspi: Exit the ISR with IRQ_NONE when it's not ours SUNRPC reverting d03727b248d0 ("NFSv4 fix CLOSE not waiting for direct IO compeletion") irqdomain/treewide: Keep firmware node unconditionally allocated drm/nouveau/i2c/g94-: increase NV_PMGR_DP_AUXCTL_TRANSACTREQ timeout net: sky2: initialize return of gm_phy_read drivers/net/wan/lapbether: Fixed the value of hard_header_len xtensa: update *pos in cpuinfo_op.next xtensa: fix __sync_fetch_and_{and,or}_4 declarations scsi: scsi_transport_spi: Fix function pointer check mac80211: allow rx of mesh eapol frames with default rx key pinctrl: amd: fix npins for uart0 in kerncz_groups gpio: arizona: put pm_runtime in case of failure gpio: arizona: handle pm_runtime_get_sync failure case ANDROID: Incremental fs: magic number compatible 32-bit ANDROID: kbuild: don't merge .*..compoundliteral in modules Revert "arm64/alternatives: use subsections for replacement sequences" Linux 4.14.189 rxrpc: Fix trace string libceph: don't omit recovery_deletes in target_copy() x86/cpu: Move x86_cache_bits settings sched/fair: handle case of task_h_load() returning 0 arm64: ptrace: Override SPSR.SS when single-stepping is enabled thermal/drivers/cpufreq_cooling: Fix wrong frequency converted from power misc: atmel-ssc: lock with mutex instead of spinlock dmaengine: fsl-edma: Fix NULL pointer exception in fsl_edma_tx_handler intel_th: pci: Add Emmitsburg PCH support intel_th: pci: Add Tiger Lake PCH-H support intel_th: pci: Add Jasper Lake CPU support hwmon: (emc2103) fix unable to change fan pwm1_enable attribute MIPS: Fix build for LTS kernel caused by backporting lpj adjustment timer: Fix wheel index calculation on last level uio_pdrv_genirq: fix use without device tree and no interrupt Input: i8042 - add Lenovo XiaoXin Air 12 to i8042 nomux list mei: bus: don't clean driver pointer Revert "zram: convert remaining CLASS_ATTR() to CLASS_ATTR_RO()" fuse: Fix parameter for FS_IOC_{GET,SET}FLAGS virtio: virtio_console: add missing MODULE_DEVICE_TABLE() for rproc serial USB: serial: option: add Quectel EG95 LTE modem USB: serial: option: add GosunCn GM500 series USB: serial: ch341: add new Product ID for CH340 USB: serial: cypress_m8: enable Simply Automated UPB PIM USB: serial: iuu_phoenix: fix memory corruption usb: gadget: function: fix missing spinlock in f_uac1_legacy usb: chipidea: core: add wakeup support for extcon usb: dwc2: Fix shutdown callback in platform USB: c67x00: fix use after free in c67x00_giveback_urb ALSA: usb-audio: Fix race against the error recovery URB submission ALSA: line6: Perform sanity check for each URB creation HID: magicmouse: do not set up autorepeat mtd: rawnand: oxnas: Release all devices in the _remove() path mtd: rawnand: oxnas: Unregister all devices on error mtd: rawnand: oxnas: Keep track of registered devices mtd: rawnand: brcmnand: fix CS0 layout perf stat: Zero all the 'ena' and 'run' array slot stats for interval mode copy_xstate_to_kernel: Fix typo which caused GDB regression ARM: dts: socfpga: Align L2 cache-controller nodename with dtschema Revert "thermal: mediatek: fix register index error" staging: comedi: verify array index is correct before using it usb: gadget: udc: atmel: fix uninitialized read in debug printk spi: spi-sun6i: sun6i_spi_transfer_one(): fix setting of clock rate arm64: dts: meson: add missing gxl rng clock phy: sun4i-usb: fix dereference of pointer phy0 before it is null checked iio:health:afe4404 Fix timestamp alignment and prevent data leak. ACPI: video: Use native backlight on Acer TravelMate 5735Z ACPI: video: Use native backlight on Acer Aspire 5783z mmc: sdhci: do not enable card detect interrupt for gpio cd type doc: dt: bindings: usb: dwc3: Update entries for disabling SS instances in park mode Revert "usb/xhci-plat: Set PM runtime as active on resume" Revert "usb/ehci-platform: Set PM runtime as active on resume" Revert "usb/ohci-platform: Fix a warning when hibernating" of: of_mdio: Correct loop scanning logic net: dsa: bcm_sf2: Fix node reference count spi: fix initial SPI_SR value in spi-fsl-dspi spi: spi-fsl-dspi: Fix lockup if device is shutdown during SPI transfer iio:health:afe4403 Fix timestamp alignment and prevent data leak. iio:pressure:ms5611 Fix buffer element alignment iio: pressure: zpa2326: handle pm_runtime_get_sync failure iio: mma8452: Add missed iio_device_unregister() call in mma8452_probe() iio: magnetometer: ak8974: Fix runtime PM imbalance on error iio:humidity:hdc100x Fix alignment and data leak issues iio:magnetometer:ak8974: Fix alignment and data leak issues arm64/alternatives: don't patch up internal branches arm64: alternative: Use true and false for boolean values i2c: eg20t: Load module automatically if ID matches gfs2: read-only mounts should grab the sd_freeze_gl glock tpm_tis: extra chip->ops check on error path in tpm_tis_core_init arm64/alternatives: use subsections for replacement sequences drm/exynos: fix ref count leak in mic_pre_enable cgroup: Fix sock_cgroup_data on big-endian. cgroup: fix cgroup_sk_alloc() for sk_clone_lock() tcp: md5: do not send silly options in SYNCOOKIES tcp: make sure listeners don't initialize congestion-control state net_sched: fix a memory leak in atm_tc_init() tcp: md5: allow changing MD5 keys in all socket states tcp: md5: refine tcp_md5_do_add()/tcp_md5_hash_key() barriers tcp: md5: add missing memory barriers in tcp_md5_do_add()/tcp_md5_hash_key() net: usb: qmi_wwan: add support for Quectel EG95 LTE modem net: Added pointer check for dst->ops->neigh_lookup in dst_neigh_lookup_skb llc: make sure applications use ARPHRD_ETHER l2tp: remove skb_dst_set() from l2tp_xmit_skb() ipv4: fill fl4_icmp_{type,code} in ping_v4_sendmsg genetlink: remove genl_bind s390/mm: fix huge pte soft dirty copying ARC: elf: use right ELF_ARCH ARC: entry: fix potential EFA clobber when TIF_SYSCALL_TRACE dm: use noio when sending kobject event drm/radeon: fix double free btrfs: fix fatal extent_buffer readahead vs releasepage race Revert "ath9k: Fix general protection fault in ath9k_hif_usb_rx_cb" KVM: x86: Mark CR4.TSD as being possibly owned by the guest KVM: x86: Inject #GP if guest attempts to toggle CR4.LA57 in 64-bit mode KVM: x86: bit 8 of non-leaf PDPEs is not reserved KVM: arm64: Stop clobbering x0 for HVC_SOFT_RESTART KVM: arm64: Fix definition of PAGE_HYP_DEVICE ALSA: usb-audio: add quirk for MacroSilicon MS2109 ALSA: hda - let hs_mic be picked ahead of hp_mic ALSA: opl3: fix infoleak in opl3 mlxsw: spectrum_router: Remove inappropriate usage of WARN_ON() net: macb: mark device wake capable when "magic-packet" property present bnxt_en: fix NULL dereference in case SR-IOV configuration fails nbd: Fix memory leak in nbd_add_socket arm64: kgdb: Fix single-step exception handling oops ALSA: compress: fix partial_drain completion state smsc95xx: avoid memory leak in smsc95xx_bind smsc95xx: check return value of smsc95xx_reset net: cxgb4: fix return error value in t4_prep_fw x86/entry: Increase entry_stack size to a full page nvme-rdma: assign completion vector correctly scsi: mptscsih: Fix read sense data size ARM: imx6: add missing put_device() call in imx6q_suspend_init() cifs: update ctime and mtime during truncate s390/kasan: fix early pgm check handler execution ixgbe: protect ring accesses with READ- and WRITE_ONCE spi: spidev: fix a potential use-after-free in spidev_release() spi: spidev: fix a race between spidev_release and spidev_remove gpu: host1x: Detach driver on unregister ARM: dts: omap4-droid4: Fix spi configuration and increase rate spi: spi-fsl-dspi: Fix external abort on interrupt in resume or exit paths spi: spi-fsl-dspi: use IRQF_SHARED mode to request IRQ spi: spi-fsl-dspi: Fix lockup if device is removed during SPI transfer spi: spi-fsl-dspi: Adding shutdown hook KVM: s390: reduce number of IO pins to 1 UPSTREAM: perf/core: Fix crash when using HW tracing kernel filters ANDROID: fscrypt: fix DUN contiguity with inline encryption + IV_INO_LBLK_32 policies ANDROID: f2fs: add back compress inode check Linux 4.14.188 efi: Make it possible to disable efivar_ssdt entirely dm zoned: assign max_io_len correctly irqchip/gic: Atomically update affinity MIPS: Add missing EHB in mtc0 -> mfc0 sequence for DSPen cifs: Fix the target file was deleted when rename failed. SMB3: Honor persistent/resilient handle flags for multiuser mounts SMB3: Honor 'seal' flag for multiuser mounts Revert "ALSA: usb-audio: Improve frames size computation" nfsd: apply umask on fs without ACL support i2c: algo-pca: Add 0x78 as SCL stuck low status for PCA9665 virtio-blk: free vblk-vqs in error path of virtblk_probe() drm: sun4i: hdmi: Remove extra HPD polling hwmon: (acpi_power_meter) Fix potential memory leak in acpi_power_meter_add() hwmon: (max6697) Make sure the OVERT mask is set correctly cxgb4: parse TC-U32 key values and masks natively cxgb4: use unaligned conversion for fetching timestamp crypto: af_alg - fix use-after-free in af_alg_accept() due to bh_lock_sock() kgdb: Avoid suspicious RCU usage warning usb: usbtest: fix missing kfree(dev->buf) in usbtest_disconnect mm/slub: fix stack overruns with SLUB_STATS mm/slub.c: fix corrupted freechain in deactivate_slab() usbnet: smsc95xx: Fix use-after-free after removal EDAC/amd64: Read back the scrub rate PCI register on F15h mm: fix swap cache node allocation mask btrfs: fix data block group relocation failure due to concurrent scrub btrfs: cow_file_range() num_bytes and disk_num_bytes are same btrfs: fix a block group ref counter leak after failure to remove block group UPSTREAM: binder: fix null deref of proc->context ANDROID: GKI: scripts: Makefile: update the lz4 command (#2) Linux 4.14.187 Revert "tty: hvc: Fix data abort due to race in hvc_open" xfs: add agf freeblocks verify in xfs_agf_verify NFSv4 fix CLOSE not waiting for direct IO compeletion pNFS/flexfiles: Fix list corruption if the mirror count changes SUNRPC: Properly set the @subbuf parameter of xdr_buf_subsegment() sunrpc: fixed rollback in rpc_gssd_dummy_populate() Staging: rtl8723bs: prevent buffer overflow in update_sta_support_rate() drm/radeon: fix fb_div check in ni_init_smc_spll_table() tracing: Fix event trigger to accept redundant spaces arm64: perf: Report the PC value in REGS_ABI_32 mode ocfs2: fix panic on nfs server over ocfs2 ocfs2: fix value of OCFS2_INVALID_SLOT ocfs2: load global_inode_alloc mm/slab: use memzero_explicit() in kzfree() btrfs: fix failure of RWF_NOWAIT write into prealloc extent beyond eof KVM: nVMX: Plumb L2 GPA through to PML emulation KVM: X86: Fix MSR range of APIC registers in X2APIC mode ACPI: sysfs: Fix pm_profile_attr type ALSA: hda: Add NVIDIA codec IDs 9a & 9d through a0 to patch table blktrace: break out of blktrace setup on concurrent calls kbuild: improve cc-option to clean up all temporary files s390/ptrace: fix setting syscall number net: alx: fix race condition in alx_remove ata/libata: Fix usage of page address by page_address in ata_scsi_mode_select_xlat function sched/core: Fix PI boosting between RT and DEADLINE tasks net: bcmgenet: use hardware padding of runt frames netfilter: ipset: fix unaligned atomic access usb: gadget: udc: Potential Oops in error handling code ARM: imx5: add missing put_device() call in imx_suspend_alloc_ocram() net: qed: fix excessive QM ILT lines consumption net: qed: fix NVMe login fails over VFs net: qed: fix left elements count calculation RDMA/mad: Fix possible memory leak in ib_mad_post_receive_mads() ASoC: rockchip: Fix a reference count leak. RDMA/cma: Protect bind_list and listen_list while finding matching cm id rxrpc: Fix handling of rwind from an ACK packet ARM: dts: NSP: Correct FA2 mailbox node efi/esrt: Fix reference count leak in esre_create_sysfs_entry. cifs/smb3: Fix data inconsistent when zero file range cifs/smb3: Fix data inconsistent when punch hole xhci: Poll for U0 after disabling USB2 LPM ALSA: usb-audio: Fix OOB access of mixer element list ALSA: usb-audio: Clean up mixer element list traverse ALSA: usb-audio: uac1: Invalidate ctl on interrupt loop: replace kill_bdev with invalidate_bdev cdc-acm: Add DISABLE_ECHO quirk for Microchip/SMSC chip xhci: Fix enumeration issue when setting max packet size for FS devices. xhci: Fix incorrect EP_STATE_MASK ALSA: usb-audio: add quirk for Denon DCD-1500RE usb: host: ehci-exynos: Fix error check in exynos_ehci_probe() usb: host: xhci-mtk: avoid runtime suspend when removing hcd USB: ehci: reopen solution for Synopsys HC bug usb: add USB_QUIRK_DELAY_INIT for Logitech C922 usb: dwc2: Postponed gadget registration to the udc class driver USB: ohci-sm501: Add missed iounmap() in remove net: core: reduce recursion limit value net: Do not clear the sock TX queue in sk_set_socket() net: Fix the arp error in some cases ip6_gre: fix use-after-free in ip6gre_tunnel_lookup() tcp_cubic: fix spurious HYSTART_DELAY exit upon drop in min RTT ip_tunnel: fix use-after-free in ip_tunnel_lookup() tg3: driver sleeps indefinitely when EEH errors exceed eeh_max_freezes tcp: grow window for OOO packets only for SACK flows sctp: Don't advertise IPv4 addresses if ipv6only is set on the socket rxrpc: Fix notification call on completion of discarded calls rocker: fix incorrect error handling in dma_rings_init net: usb: ax88179_178a: fix packet alignment padding net: fix memleak in register_netdevice() net: bridge: enfore alignment for ethernet address mld: fix memory leak in ipv6_mc_destroy_dev() ibmveth: Fix max MTU limit apparmor: don't try to replace stale label in ptraceme check fix a braino in "sparc32: fix register window handling in genregs32_[gs]et()" net: sched: export __netdev_watchdog_up() block/bio-integrity: don't free 'buf' if bio_integrity_add_page() failed net: be more gentle about silly gso requests coming from user scsi: scsi_devinfo: handle non-terminated strings ANDROID: Makefile: append BUILD_NUMBER to version string when defined Linux 4.14.186 KVM: x86/mmu: Set mmio_value to '0' if reserved #PF can't be generated kvm: x86: Fix reserved bits related calculation errors caused by MKTME kvm: x86: Move kvm_set_mmio_spte_mask() from x86.c to mmu.c md: add feature flag MD_FEATURE_RAID0_LAYOUT net: core: device_rename: Use rwsem instead of a seqcount sched/rt, net: Use CONFIG_PREEMPTION.patch kretprobe: Prevent triggering kretprobe from within kprobe_flush_task e1000e: Do not wake up the system via WOL if device wakeup is disabled kprobes: Fix to protect kick_kprobe_optimizer() by kprobe_mutex crypto: algboss - don't wait during notifier callback crypto: algif_skcipher - Cap recv SG list at ctx->used mtd: rawnand: tmio: Fix the probe error path mtd: rawnand: mtk: Fix the probe error path mtd: rawnand: plat_nand: Fix the probe error path mtd: rawnand: socrates: Fix the probe error path mtd: rawnand: oxnas: Fix the probe error path mtd: rawnand: oxnas: Add of_node_put() mtd: rawnand: orion: Fix the probe error path mtd: rawnand: xway: Fix the probe error path mtd: rawnand: sharpsl: Fix the probe error path mtd: rawnand: diskonchip: Fix the probe error path mtd: rawnand: Pass a nand_chip object to nand_release() block: nr_sects_write(): Disable preemption on seqcount write x86/boot/compressed: Relax sed symbol type regex for LLVM ld.lld drm/dp_mst: Increase ACT retry timeout to 3s ext4: fix partial cluster initialization when splitting extent selinux: fix double free drm/qxl: Use correct notify port address when creating cursor ring drm/dp_mst: Reformat drm_dp_check_act_status() a bit drm: encoder_slave: fix refcouting error for modules libata: Use per port sync for detach arm64: hw_breakpoint: Don't invoke overflow handler on uaccess watchpoints block: Fix use-after-free in blkdev_get() bcache: fix potential deadlock problem in btree_gc_coalesce perf report: Fix NULL pointer dereference in hists__fprintf_nr_sample_events() usb/ehci-platform: Set PM runtime as active on resume usb/xhci-plat: Set PM runtime as active on resume scsi: acornscsi: Fix an error handling path in acornscsi_probe() drm/sun4i: hdmi ddc clk: Fix size of m divider selftests/net: in timestamping, strncpy needs to preserve null byte gfs2: fix use-after-free on transaction ail lists blktrace: fix endianness for blk_log_remap() blktrace: fix endianness in get_pdu_int() blktrace: use errno instead of bi_status selftests/vm/pkeys: fix alloc_random_pkey() to make it really random elfnote: mark all .note sections SHF_ALLOC include/linux/bitops.h: avoid clang shift-count-overflow warnings lib/zlib: remove outdated and incorrect pre-increment optimization geneve: change from tx_error to tx_dropped on missing metadata crypto: omap-sham - add proper load balancing support for multicore pinctrl: freescale: imx: Fix an error handling path in 'imx_pinctrl_probe()' pinctrl: imxl: Fix an error handling path in 'imx1_pinctrl_core_probe()' scsi: ufs: Don't update urgent bkops level when toggling auto bkops scsi: iscsi: Fix reference count leak in iscsi_boot_create_kobj gfs2: Allow lock_nolock mount to specify jid=X openrisc: Fix issue with argument clobbering for clone/fork vfio/mdev: Fix reference count leak in add_mdev_supported_type ASoC: fsl_asrc_dma: Fix dma_chan leak when config DMA channel failed extcon: adc-jack: Fix an error handling path in 'adc_jack_probe()' powerpc/4xx: Don't unmap NULL mbase NFSv4.1 fix rpc_call_done assignment for BIND_CONN_TO_SESSION net: sunrpc: Fix off-by-one issues in 'rpc_ntop6' scsi: ufs-qcom: Fix scheduling while atomic issue clk: bcm2835: Fix return type of bcm2835_register_gate x86/apic: Make TSC deadline timer detection message visible usb: gadget: Fix issue with config_ep_by_speed function usb: gadget: fix potential double-free in m66592_probe. usb: gadget: lpc32xx_udc: don't dereference ep pointer before null check USB: gadget: udc: s3c2410_udc: Remove pointless NULL check in s3c2410_udc_nuke usb: dwc2: gadget: move gadget resume after the core is in L0 state watchdog: da9062: No need to ping manually before setting timeout IB/cma: Fix ports memory leak in cma_configfs PCI/PTM: Inherit Switch Downstream Port PTM settings from Upstream Port dm zoned: return NULL if dmz_get_zone_for_reclaim() fails to find a zone powerpc/64s/pgtable: fix an undefined behaviour clk: samsung: exynos5433: Add IGNORE_UNUSED flag to sclk_i2s1 tty: n_gsm: Fix bogus i++ in gsm_data_kick USB: host: ehci-mxc: Add error handling in ehci_mxc_drv_probe() drm/msm/mdp5: Fix mdp5_init error path for failed mdp5_kms allocation usb/ohci-platform: Fix a warning when hibernating vfio-pci: Mask cap zero powerpc/ps3: Fix kexec shutdown hang powerpc/pseries/ras: Fix FWNMI_VALID off by one tty: n_gsm: Fix waking up upper tty layer when room available tty: n_gsm: Fix SOF skipping PCI: Fix pci_register_host_bridge() device_register() error handling clk: ti: composite: fix memory leak dlm: remove BUG() before panic() scsi: mpt3sas: Fix double free warnings power: supply: smb347-charger: IRQSTAT_D is volatile power: supply: lp8788: Fix an error handling path in 'lp8788_charger_probe()' scsi: qla2xxx: Fix warning after FC target reset PCI/ASPM: Allow ASPM on links to PCIe-to-PCI/PCI-X Bridges PCI: rcar: Fix incorrect programming of OB windows drivers: base: Fix NULL pointer exception in __platform_driver_probe() if a driver developer is foolish serial: amba-pl011: Make sure we initialize the port.lock spinlock i2c: pxa: fix i2c_pxa_scream_blue_murder() debug output staging: sm750fb: add missing case while setting FB_VISUAL thermal/drivers/ti-soc-thermal: Avoid dereferencing ERR_PTR tty: hvc: Fix data abort due to race in hvc_open s390/qdio: put thinint indicator after early error ALSA: usb-audio: Improve frames size computation scsi: qedi: Do not flush offload work if ARP not resolved staging: greybus: fix a missing-check bug in gb_lights_light_config() scsi: ibmvscsi: Don't send host info in adapter info MAD after LPM scsi: sr: Fix sr_probe() missing deallocate of device minor apparmor: fix introspection of of task mode for unconfined tasks mksysmap: Fix the mismatch of '.L' symbols in System.map NTB: Fix the default port and peer numbers for legacy drivers yam: fix possible memory leak in yam_init_driver powerpc/crashkernel: Take "mem=" option into account nfsd: Fix svc_xprt refcnt leak when setup callback client failed powerpc/perf/hv-24x7: Fix inconsistent output values incase multiple hv-24x7 events run clk: clk-flexgen: fix clock-critical handling scsi: lpfc: Fix lpfc_nodelist leak when processing unsolicited event mfd: wm8994: Fix driver operation if loaded as modules m68k/PCI: Fix a memory leak in an error handling path vfio/pci: fix memory leaks in alloc_perm_bits() ps3disk: use the default segment boundary PCI: aardvark: Don't blindly enable ASPM L0s and don't write to read-only register dm mpath: switch paths in dm_blk_ioctl() code path usblp: poison URBs upon disconnect i2c: pxa: clear all master action bits in i2c_pxa_stop_message() f2fs: report delalloc reserve as non-free in statfs for project quota iio: bmp280: fix compensation of humidity scsi: qla2xxx: Fix issue with adapter's stopping state ALSA: isa/wavefront: prevent out of bounds write in ioctl scsi: qedi: Check for buffer overflow in qedi_set_path() ARM: integrator: Add some Kconfig selections ASoC: davinci-mcasp: Fix dma_chan refcnt leak when getting dma type backlight: lp855x: Ensure regulators are disabled on probe failure clk: qcom: msm8916: Fix the address location of pll->config_reg remoteproc: Fix IDR initialisation in rproc_alloc() iio: pressure: bmp280: Tolerate IRQ before registering i2c: piix4: Detect secondary SMBus controller on AMD AM4 chipsets clk: sunxi: Fix incorrect usage of round_down() power: supply: bq24257_charger: Replace depends on REGMAP_I2C with select drm/i915: Whitelist context-local timestamp in the gen9 cmdparser s390: fix syscall_get_error for compat processes ANDROID: ext4: Optimize match for casefolded encrypted dirs ANDROID: ext4: Handle casefolding with encryption ANDROID: cuttlefish_defconfig: x86: Enable KERNEL_LZ4 ANDROID: GKI: scripts: Makefile: update the lz4 command FROMLIST: f2fs: fix use-after-free when accessing bio->bi_crypt_context Linux 4.14.185 perf symbols: Fix debuginfo search for Ubuntu perf probe: Fix to check blacklist address correctly perf probe: Do not show the skipped events w1: omap-hdq: cleanup to add missing newline for some dev_dbg mtd: rawnand: pasemi: Fix the probe error path mtd: rawnand: brcmnand: fix hamming oob layout sunrpc: clean up properly in gss_mech_unregister() sunrpc: svcauth_gss_register_pseudoflavor must reject duplicate registrations. kbuild: force to build vmlinux if CONFIG_MODVERSION=y powerpc/64s: Save FSCR to init_task.thread.fscr after feature init powerpc/64s: Don't let DT CPU features set FSCR_DSCR drivers/macintosh: Fix memleak in windfarm_pm112 driver ARM: tegra: Correct PL310 Auxiliary Control Register initialization kernel/cpu_pm: Fix uninitted local in cpu_pm dm crypt: avoid truncating the logical block size sparc64: fix misuses of access_process_vm() in genregs32_[sg]et() sparc32: fix register window handling in genregs32_[gs]et() pinctrl: samsung: Save/restore eint_mask over suspend for EINT_TYPE GPIOs power: vexpress: add suppress_bind_attrs to true igb: Report speed and duplex as unknown when device is runtime suspended media: ov5640: fix use of destroyed mutex b43_legacy: Fix connection problem with WPA3 b43: Fix connection problem with WPA3 b43legacy: Fix case where channel status is corrupted media: go7007: fix a miss of snd_card_free carl9170: remove P2P_GO support e1000e: Relax condition to trigger reset for ME workaround e1000e: Disable TSO for buffer overrun workaround PCI: Program MPS for RCiEP devices blk-mq: move _blk_mq_update_nr_hw_queues synchronize_rcu call btrfs: fix wrong file range cleanup after an error filling dealloc range btrfs: fix error handling when submitting direct I/O bio PCI: Unify ACS quirk desired vs provided checking PCI: Add ACS quirk for Intel Root Complex Integrated Endpoints PCI: Generalize multi-function power dependency device links vga_switcheroo: Use device link for HDA controller vga_switcheroo: Deduplicate power state tracking PCI: Make ACS quirk implementations more uniform PCI: Add ACS quirk for Ampere root ports PCI: Add ACS quirk for iProc PAXB PCI: Avoid FLR for AMD Starship USB 3.0 PCI: Avoid FLR for AMD Matisse HD Audio & USB 3.0 PCI: Disable MSI for Freescale Layerscape PCIe RC mode ext4: fix race between ext4_sync_parent() and rename() ext4: fix error pointer dereference ext4: fix EXT_MAX_EXTENT/INDEX to check for zeroed eh_max evm: Fix possible memory leak in evm_calc_hmac_or_hash() ima: Directly assign the ima_default_policy pointer to ima_rules ima: Fix ima digest hash table key calculation mm: thp: make the THP mapcount atomic against __split_huge_pmd_locked() btrfs: send: emit file capabilities after chown string.h: fix incompatibility between FORTIFY_SOURCE and KASAN platform/x86: hp-wmi: Convert simple_strtoul() to kstrtou32() cpuidle: Fix three reference count leaks spi: dw: Return any value retrieved from the dma_transfer callback mmc: sdhci-esdhc-imx: fix the mask for tuning start point ixgbe: fix signed-integer-overflow warning mmc: via-sdmmc: Respect the cmd->busy_timeout from the mmc core staging: greybus: sdio: Respect the cmd->busy_timeout from the mmc core mmc: sdhci-msm: Set SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 quirk MIPS: Fix IRQ tracing when call handle_fpe() and handle_msa_fpe() PCI: Don't disable decoding when mmio_always_on is set macvlan: Skip loopback packets in RX handler m68k: mac: Don't call via_flush_cache() on Mac IIfx x86/mm: Stop printing BRK addresses mips: Add udelay lpj numbers adjustment mips: MAAR: Use more precise address mask x86/boot: Correct relocation destination on old linkers mwifiex: Fix memory corruption in dump_station rtlwifi: Fix a double free in _rtl_usb_tx_urb_setup() md: don't flush workqueue unconditionally in md_open net: qed*: Reduce RX and TX default ring count when running inside kdump kernel wcn36xx: Fix error handling path in 'wcn36xx_probe()' nvme: refine the Qemu Identify CNS quirk kgdb: Fix spurious true from in_dbg_master() mips: cm: Fix an invalid error code of INTVN_*_ERR MIPS: Truncate link address into 32bit for 32bit kernel Crypto/chcr: fix for ccm(aes) failed test powerpc/spufs: fix copy_to_user while atomic net: allwinner: Fix use correct return type for ndo_start_xmit() media: cec: silence shift wrapping warning in __cec_s_log_addrs() net: lpc-enet: fix error return code in lpc_mii_init() exit: Move preemption fixup up, move blocking operations down lib/mpi: Fix 64-bit MIPS build with Clang net: bcmgenet: set Rx mode before starting netif netfilter: nft_nat: return EOPNOTSUPP if type or flags are not supported audit: fix a net reference leak in audit_list_rules_send() MIPS: Make sparse_init() using top-down allocation media: platform: fcp: Set appropriate DMA parameters media: dvb: return -EREMOTEIO on i2c transfer failure. audit: fix a net reference leak in audit_send_reply() dt-bindings: display: mediatek: control dpi pins mode to avoid leakage e1000: Distribute switch variables for initialization tools api fs: Make xxx__mountpoint() more scalable brcmfmac: fix wrong location to get firmware feature staging: android: ion: use vmap instead of vm_map_ram net: vmxnet3: fix possible buffer overflow caused by bad DMA value in vmxnet3_get_rss() x86/kvm/hyper-v: Explicitly align hcall param for kvm_hyperv_exit spi: dw: Fix Rx-only DMA transfers ARM: 8978/1: mm: make act_mm() respect THREAD_SIZE btrfs: do not ignore error from btrfs_next_leaf() when inserting checksums clocksource: dw_apb_timer_of: Fix missing clockevent timers clocksource: dw_apb_timer: Make CPU-affiliation being optional spi: dw: Enable interrupts in accordance with DMA xfer mode kgdb: Prevent infinite recursive entries to the debugger Bluetooth: Add SCO fallback for invalid LMP parameters error MIPS: Loongson: Build ATI Radeon GPU driver as module ixgbe: Fix XDP redirect on archs with PAGE_SIZE above 4K spi: dw: Zero DMA Tx and Rx configurations on stack net: ena: fix error returning in ena_com_get_hash_function() spi: pxa2xx: Apply CS clk quirk to BXT objtool: Ignore empty alternatives media: si2157: Better check for running tuner in init crypto: ccp -- don't "select" CONFIG_DMADEVICES drm: bridge: adv7511: Extend list of audio sample rates ACPI: GED: use correct trigger type field in _Exx / _Lxx handling xen/pvcalls-back: test for errors when calling backend_connect() can: kvaser_usb: kvaser_usb_leaf: Fix some info-leaks to USB devices mmc: sdio: Fix potential NULL pointer error in mmc_sdio_init_card() mmc: sdhci-msm: Clear tuning done flag while hs400 tuning agp/intel: Reinforce the barrier after GTT updates perf: Add cond_resched() to task_function_call() fat: don't allow to mount if the FAT length == 0 mm/slub: fix a memory leak in sysfs_slab_add() Smack: slab-out-of-bounds in vsscanf ath9k: Fix general protection fault in ath9k_hif_usb_rx_cb ath9x: Fix stack-out-of-bounds Write in ath9k_hif_usb_rx_cb ath9k: Fix use-after-free Write in ath9k_htc_rx_msg ath9k: Fix use-after-free Read in ath9k_wmi_ctrl_rx KVM: arm64: Make vcpu_cp1x() work on Big Endian hosts KVM: MIPS: Fix VPN2_MASK definition for variable cpu_vmbits KVM: MIPS: Define KVM_ENTRYHI_ASID to cpu_asid_mask(&boot_cpu_data) KVM: nVMX: Consult only the "basic" exit reason when routing nested exit KVM: nSVM: leave ASID aside in copy_vmcb_control_area KVM: nSVM: fix condition for filtering async PF video: fbdev: w100fb: Fix a potential double free. proc: Use new_inode not new_inode_pseudo ovl: initialize error in ovl_copy_xattr selftests/net: in rxtimestamp getopt_long needs terminating null entry crypto: virtio: Fix dest length calculation in __virtio_crypto_skcipher_do_req() crypto: virtio: Fix src/dst scatterlist calculation in __virtio_crypto_skcipher_do_req() crypto: virtio: Fix use-after-free in virtio_crypto_skcipher_finalize_req() spi: bcm2835: Fix controller unregister order spi: pxa2xx: Fix controller unregister order spi: Fix controller unregister order spi: No need to assign dummy value in spi_unregister_controller() spi: dw: Fix controller unregister order spi: dw: fix possible race condition x86/speculation: PR_SPEC_FORCE_DISABLE enforcement for indirect branches. x86/speculation: Avoid force-disabling IBPB based on STIBP and enhanced IBRS. x86/speculation: Add support for STIBP always-on preferred mode x86/speculation: Change misspelled STIPB to STIBP KVM: x86: only do L1TF workaround on affected processors KVM: x86/mmu: Consolidate "is MMIO SPTE" code kvm: x86: Fix L1TF mitigation for shadow MMU ALSA: pcm: disallow linking stream to itself crypto: cavium/nitrox - Fix 'nitrox_get_first_device()' when ndevlist is fully iterated spi: bcm-qspi: when tx/rx buffer is NULL set to 0 spi: bcm2835aux: Fix controller unregister order nilfs2: fix null pointer dereference at nilfs_segctor_do_construct() cgroup, blkcg: Prepare some symbols for module and !CONFIG_CGROUP usages ACPI: PM: Avoid using power resources if there are none for D0 ACPI: GED: add support for _Exx / _Lxx handler methods ACPI: CPPC: Fix reference count leak in acpi_cppc_processor_probe() ACPI: sysfs: Fix reference count leak in acpi_sysfs_add_hotplug_profile() ALSA: usb-audio: Fix inconsistent card PM state after resume ALSA: hda/realtek - add a pintbl quirk for several Lenovo machines ALSA: es1688: Add the missed snd_card_free() efi/efivars: Add missing kobject_put() in sysfs entry creation error path x86/reboot/quirks: Add MacBook6,1 reboot quirk x86/speculation: Prevent rogue cross-process SSBD shutdown x86/PCI: Mark Intel C620 MROMs as having non-compliant BARs x86_64: Fix jiffies ODR violation mm: add kvfree_sensitive() for freeing sensitive data objects perf probe: Accept the instance number of kretprobe event ath9k_htc: Silence undersized packet warnings powerpc/xive: Clear the page tables for the ESB IO mapping drivers/net/ibmvnic: Update VNIC protocol version reporting Input: synaptics - add a second working PNP_ID for Lenovo T470s sched/fair: Don't NUMA balance for kthreads ARM: 8977/1: ptrace: Fix mask for thumb breakpoint hook crypto: talitos - fix ECB and CBC algs ivsize serial: imx: Fix handling of TC irq in combination with DMA lib: Reduce user_access_begin() boundaries in strncpy_from_user() and strnlen_user() x86: uaccess: Inhibit speculation past access_ok() in user_access_begin() arch/openrisc: Fix issues with access_ok() Fix 'acccess_ok()' on alpha and SH make 'user_access_begin()' do 'access_ok()' vxlan: Avoid infinite loop when suppressing NS messages with invalid options ipv6: fix IPV6_ADDRFORM operation logic writeback: Drop I_DIRTY_TIME_EXPIRE writeback: Fix sync livelock due to b_dirty_time processing writeback: Avoid skipping inode writeback writeback: Protect inode->i_io_list with inode->i_lock Revert "writeback: Avoid skipping inode writeback" ANDROID: Enable LZ4_RAMDISK fscrypt: remove stale definition fs-verity: remove unnecessary extern keywords fs-verity: fix all kerneldoc warnings fscrypt: add support for IV_INO_LBLK_32 policies fscrypt: make test_dummy_encryption use v2 by default fscrypt: support test_dummy_encryption=v2 fscrypt: add fscrypt_add_test_dummy_key() linux/parser.h: add include guards fscrypt: remove unnecessary extern keywords fscrypt: name all function parameters fscrypt: fix all kerneldoc warnings ANDROID: kbuild: merge more sections with LTO Linux 4.14.184 uprobes: ensure that uprobe->offset and ->ref_ctr_offset are properly aligned iio: vcnl4000: Fix i2c swapped word reading. x86/speculation: Add Ivy Bridge to affected list x86/speculation: Add SRBDS vulnerability and mitigation documentation x86/speculation: Add Special Register Buffer Data Sampling (SRBDS) mitigation x86/cpu: Add 'table' argument to cpu_matches() x86/cpu: Add a steppings field to struct x86_cpu_id nvmem: qfprom: remove incorrect write support CDC-ACM: heed quirk also in error handling staging: rtl8712: Fix IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK tty: hvc_console, fix crashes on parallel open/close vt: keyboard: avoid signed integer overflow in k_ascii usb: musb: Fix runtime PM imbalance on error usb: musb: start session in resume for host port USB: serial: option: add Telit LE910C1-EUX compositions USB: serial: usb_wwan: do not resubmit rx urb on fatal errors USB: serial: qcserial: add DW5816e QDL support l2tp: add sk_family checks to l2tp_validate_socket net: check untrusted gso_size at kernel entry vsock: fix timeout in vsock_accept() NFC: st21nfca: add missed kfree_skb() in an error path net: usb: qmi_wwan: add Telit LE910C1-EUX composition l2tp: do not use inet_hash()/inet_unhash() devinet: fix memleak in inetdev_init() airo: Fix read overflows sending packets scsi: ufs: Release clock if DMA map fails mmc: fix compilation of user API kernel/relay.c: handle alloc_percpu returning NULL in relay_open p54usb: add AirVasT USB stick device-id HID: i2c-hid: add Schneider SCL142ALM to descriptor override HID: sony: Fix for broken buttons on DS3 USB dongles mm: Fix mremap not considering huge pmd devmap net: smsc911x: Fix runtime PM imbalance on error net: ethernet: stmmac: Enable interface clocks on probe for IPQ806x net/ethernet/freescale: rework quiesce/activate for ucc_geth net: bmac: Fix read of MAC address from ROM x86/mmiotrace: Use cpumask_available() for cpumask_var_t variables i2c: altera: Fix race between xfer_msg and isr thread ARC: [plat-eznps]: Restrict to CONFIG_ISA_ARCOMPACT ARC: Fix ICCM & DCCM runtime size checks pppoe: only process PADT targeted at local interfaces s390/ftrace: save traced function caller spi: dw: use "smp_mb()" to avoid sending spi data error scsi: hisi_sas: Check sas_port before using it libnvdimm: Fix endian conversion issues scsi: scsi_devinfo: fixup string compare ANDROID: Incremental fs: Remove dependency on PKCS7_MESSAGE_PARSER f2fs: attach IO flags to the missing cases f2fs: add node_io_flag for bio flags likewise data_io_flag f2fs: remove unused parameter of f2fs_put_rpages_mapping() f2fs: handle readonly filesystem in f2fs_ioc_shutdown() f2fs: avoid utf8_strncasecmp() with unstable name f2fs: don't return vmalloc() memory from f2fs_kmalloc() ANDROID: dm-bow: Add block_size option ANDROID: Incremental fs: Cache successful hash calculations ANDROID: Incremental fs: Fix four error-path bugs ANDROID: cuttlefish_defconfig: Disable CMOS RTC driver f2fs: fix retry logic in f2fs_write_cache_pages() ANDROID: modules: fix lockprove warning BACKPORT: arm64: vdso: Explicitly add build-id option BACKPORT: arm64: vdso: use $(LD) instead of $(CC) to link VDSO Linux 4.14.183 scsi: zfcp: fix request object use-after-free in send path causing wrong traces genirq/generic_pending: Do not lose pending affinity update net: hns: Fixes the missing put_device in positive leg for roce reset net: hns: fix unsigned comparison to less than zero KVM: VMX: check for existence of secondary exec controls before accessing rxrpc: Fix transport sockopts to get IPv4 errors on an IPv6 socket sc16is7xx: move label 'err_spi' to correct section mm/vmalloc.c: don't dereference possible NULL pointer in __vunmap() netfilter: nf_conntrack_pptp: fix compilation warning with W=1 build bonding: Fix reference count leak in bond_sysfs_slave_add. qlcnic: fix missing release in qlcnic_83xx_interrupt_test. esp6: get the right proto for transport mode in esp6_gso_encap netfilter: nf_conntrack_pptp: prevent buffer overflows in debug code netfilter: nfnetlink_cthelper: unbreak userspace helper support netfilter: ipset: Fix subcounter update skip netfilter: nft_reject_bridge: enable reject with bridge vlan ip_vti: receive ipip packet by calling ip_tunnel_rcv vti4: eliminated some duplicate code. xfrm: fix error in comment xfrm: fix a NULL-ptr deref in xfrm_local_error xfrm: fix a warning in xfrm_policy_insert_list xfrm: call xfrm_output_gso when inner_protocol is set in xfrm_output xfrm: allow to accept packets with ipv6 NEXTHDR_HOP in xfrm_input copy_xstate_to_kernel(): don't leave parts of destination uninitialized x86/dma: Fix max PFN arithmetic overflow on 32 bit systems mac80211: mesh: fix discovery timer re-arming issue / crash parisc: Fix kernel panic in mem_init() iommu: Fix reference count leak in iommu_group_alloc. include/asm-generic/topology.h: guard cpumask_of_node() macro argument fs/binfmt_elf.c: allocate initialized memory in fill_thread_core_info() mm: remove VM_BUG_ON(PageSlab()) from page_mapcount() libceph: ignore pool overlay and cache logic on redirects ALSA: hda/realtek - Add new codec supported for ALC287 exec: Always set cap_ambient in cap_bprm_set_creds ALSA: usb-audio: mixer: volume quirk for ESS Technology Asus USB DAC ALSA: hwdep: fix a left shifting 1 by 31 UB bug RDMA/pvrdma: Fix missing pci disable in pvrdma_pci_probe() mmc: block: Fix use-after-free issue for rpmb ARM: dts: bcm2835-rpi-zero-w: Fix led polarity ARM: dts/imx6q-bx50v3: Set display interface clock parents ARM: dts: imx6q-bx50v3: Add internal switch IB/qib: Call kobject_put() when kobject_init_and_add() fails gpio: exar: Fix bad handling for ida_simple_get error path ARM: uaccess: fix DACR mismatch with nested exceptions ARM: uaccess: integrate uaccess_save and uaccess_restore ARM: uaccess: consolidate uaccess asm to asm/uaccess-asm.h ARM: 8843/1: use unified assembler in headers Input: synaptics-rmi4 - fix error return code in rmi_driver_probe() Input: synaptics-rmi4 - really fix attn_data use-after-free Input: i8042 - add ThinkPad S230u to i8042 reset list Input: dlink-dir685-touchkeys - fix a typo in driver name Input: xpad - add custom init packet for Xbox One S controllers Input: evdev - call input_flush_device() on release(), not flush() Input: usbtouchscreen - add support for BonXeon TP samples: bpf: Fix build error cifs: Fix null pointer check in cifs_read net: freescale: select CONFIG_FIXED_PHY where needed usb: gadget: legacy: fix redundant initialization warnings cachefiles: Fix race between read_waiter and read_copier involving op->to_do gfs2: move privileged user check to gfs2_quota_lock_check net: microchip: encx24j600: add missed kthread_stop gpio: tegra: mask GPIO IRQs during IRQ shutdown ARM: dts: rockchip: fix pinctrl sub nodename for spi in rk322x.dtsi arm64: dts: rockchip: swap interrupts interrupt-names rk3399 gpu node ARM: dts: rockchip: fix phy nodename for rk3228-evb net/mlx4_core: fix a memory leak bug. net: sun: fix missing release regions in cas_init_one(). net: qrtr: Fix passing invalid reference to qrtr_local_enqueue() net/mlx5e: Update netdev txq on completions during closure sctp: Start shutdown on association restart if in SHUTDOWN-SENT state and socket is closed r8152: support additional Microsoft Surface Ethernet Adapter variant net sched: fix reporting the first-time use timestamp net: revert "net: get rid of an signed integer overflow in ip_idents_reserve()" net/mlx5: Add command entry handling completion net: ipip: fix wrong address family in init error path ax25: fix setsockopt(SO_BINDTODEVICE) ANDROID: scs: fix recursive spinlock in scs_check_usage ANDROID: timer: fix timer_setup with CFI FROMGIT: USB: dummy-hcd: use configurable endpoint naming scheme UPSTREAM: USB: dummy-hcd: remove unsupported isochronous endpoints UPSTREAM: usb: raw-gadget: fix null-ptr-deref when reenabling endpoints UPSTREAM: usb: raw-gadget: documentation updates UPSTREAM: usb: raw-gadget: support stalling/halting/wedging endpoints UPSTREAM: usb: raw-gadget: fix gadget endpoint selection UPSTREAM: usb: raw-gadget: improve uapi headers comments UPSTREAM: usb: raw-gadget: fix return value of ep read ioctls UPSTREAM: usb: raw-gadget: fix raw_event_queue_fetch locking UPSTREAM: usb: raw-gadget: Fix copy_to/from_user() checks f2fs: fix wrong discard space f2fs: compress: don't compress any datas after cp stop f2fs: remove unneeded return value of __insert_discard_tree() f2fs: fix wrong value of tracepoint parameter f2fs: protect new segment allocation in expand_inode_data f2fs: code cleanup by removing ifdef macro surrounding writeback: Avoid skipping inode writeback ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu Revert "ANDROID: Incremental fs: Avoid continually recalculating hashes" Linux 4.14.182 iio: adc: stm32-adc: fix device used to request dma iio: adc: stm32-adc: Use dma_request_chan() instead dma_request_slave_channel() x86/unwind/orc: Fix unwind_get_return_address_ptr() for inactive tasks rxrpc: Fix a memory leak in rxkad_verify_response() rapidio: fix an error in get_user_pages_fast() error handling mei: release me_cl object reference iio: dac: vf610: Fix an error handling path in 'vf610_dac_probe()' iio: sca3000: Remove an erroneous 'get_device()' staging: greybus: Fix uninitialized scalar variable staging: iio: ad2s1210: Fix SPI reading Revert "gfs2: Don't demote a glock until its revokes are written" cxgb4/cxgb4vf: Fix mac_hlist initialization and free cxgb4: free mac_hlist properly media: fdp1: Fix R-Car M3-N naming in debug message libnvdimm/btt: Fix LBA masking during 'free list' population libnvdimm/btt: Remove unnecessary code in btt_freelist_init ubsan: build ubsan.c more conservatively x86/uaccess, ubsan: Fix UBSAN vs. SMAP powerpc/64s: Disable STRICT_KERNEL_RWX powerpc: Remove STRICT_KERNEL_RWX incompatibility with RELOCATABLE powerpc: restore alphabetic order in Kconfig dmaengine: tegra210-adma: Fix an error handling path in 'tegra_adma_probe()' apparmor: Fix aa_label refcnt leak in policy_update ALSA: pcm: fix incorrect hw_base increase ALSA: iec1712: Initialize STDSP24 properly when using the model=staudio option l2tp: initialise PPP sessions before registering them l2tp: protect sock pointer of struct pppol2tp_session with RCU l2tp: initialise l2tp_eth sessions before registering them l2tp: don't register sessions in l2tp_session_create() arm64: fix the flush_icache_range arguments in machine_kexec padata: purge get_cpu and reorder_via_wq from padata_do_serial padata: initialize pd->cpu with effective cpumask padata: Replace delayed timer with immediate workqueue in padata_reorder padata: set cpu_index of unused CPUs to -1 ARM: futex: Address build warning platform/x86: asus-nb-wmi: Do not load on Asus T100TA and T200TA USB: core: Fix misleading driver bug report ceph: fix double unlock in handle_cap_export() gtp: set NLM_F_MULTI flag in gtp_genl_dump_pdp() x86/apic: Move TSC deadline timer debug printk scsi: ibmvscsi: Fix WARN_ON during event pool release component: Silence bind error on -EPROBE_DEFER vhost/vsock: fix packet delivery order to monitoring devices configfs: fix config_item refcnt leak in configfs_rmdir() scsi: qla2xxx: Fix hang when issuing nvme disconnect-all in NPIV HID: multitouch: add eGalaxTouch P80H84 support gcc-common.h: Update for GCC 10 ubi: Fix seq_file usage in detailed_erase_block_info debugfs file i2c: mux: demux-pinctrl: Fix an error handling path in 'i2c_demux_pinctrl_probe()' iommu/amd: Fix over-read of ACPI UID from IVRS table fix multiplication overflow in copy_fdtable() ima: Fix return value of ima_write_policy() evm: Check also if *tfm is an error pointer in init_desc() ima: Set file->f_mode instead of file->f_flags in ima_calc_file_hash() padata: ensure padata_do_serial() runs on the correct CPU padata: ensure the reorder timer callback runs on the correct CPU i2c: dev: Fix the race between the release of i2c_dev and cdev watchdog: Fix the race between the release of watchdog_core_data and cdev ext4: add cond_resched() to ext4_protect_reserved_inode ANDROID: scsi: ufs: Handle clocks when lrbp fails ANDROID: fscrypt: handle direct I/O with IV_INO_LBLK_32 BACKPORT: FROMLIST: fscrypt: add support for IV_INO_LBLK_32 policies f2fs: avoid inifinite loop to wait for flushing node pages at cp_error ANDROID: namespace'ify tcp_default_init_rwnd implementation Linux 4.14.181 Makefile: disallow data races on gcc-10 as well KVM: x86: Fix off-by-one error in kvm_vcpu_ioctl_x86_setup_mce ARM: dts: r8a7740: Add missing extal2 to CPG node ARM: dts: r8a73a4: Add missing CMT1 interrupts arm64: dts: rockchip: Rename dwc3 device nodes on rk3399 to make dtc happy arm64: dts: rockchip: Replace RK805 PMIC node name with "pmic" on rk3328 boards Revert "ALSA: hda/realtek: Fix pop noise on ALC225" usb: gadget: legacy: fix error return code in cdc_bind() usb: gadget: legacy: fix error return code in gncm_bind() usb: gadget: audio: Fix a missing error return value in audio_bind() usb: gadget: net2272: Fix a memory leak in an error handling path in 'net2272_plat_probe()' clk: rockchip: fix incorrect configuration of rk3228 aclk_gpu* clocks exec: Move would_dump into flush_old_exec x86/unwind/orc: Fix error handling in __unwind_start() usb: xhci: Fix NULL pointer dereference when enqueuing trbs from urb sg list USB: gadget: fix illegal array access in binding with UDC usb: host: xhci-plat: keep runtime active when removing host usb: core: hub: limit HUB_QUIRK_DISABLE_AUTOSUSPEND to USB5534B ALSA: usb-audio: Add control message quirk delay for Kingston HyperX headset x86: Fix early boot crash on gcc-10, third try ARM: dts: imx27-phytec-phycard-s-rdk: Fix the I2C1 pinctrl entries ARM: dts: dra7: Fix bus_dma_limit for PCIe ALSA: rawmidi: Fix racy buffer resize under concurrent accesses ALSA: rawmidi: Initialize allocated buffers ALSA: hda/realtek - Limit int mic boost for Thinkpad T530 net: tcp: fix rx timestamp behavior for tcp_recvmsg netprio_cgroup: Fix unlimited memory leak of v2 cgroups net: ipv4: really enforce backoff for redirects net: dsa: loop: Add module soft dependency hinic: fix a bug of ndo_stop Revert "ipv6: add mtu lock check in __ip6_rt_update_pmtu" net: phy: fix aneg restart in phy_ethtool_set_eee netlabel: cope with NULL catmap net: fix a potential recursive NETDEV_FEAT_CHANGE net: phy: micrel: Use strlcpy() for ethtool::get_strings x86/asm: Add instruction suffixes to bitops gcc-10: avoid shadowing standard library 'free()' in crypto gcc-10: disable 'restrict' warning for now gcc-10: disable 'stringop-overflow' warning for now gcc-10: disable 'array-bounds' warning for now gcc-10: disable 'zero-length-bounds' warning for now Stop the ad-hoc games with -Wno-maybe-initialized kbuild: compute false-positive -Wmaybe-uninitialized cases in Kconfig gcc-10 warnings: fix low-hanging fruit pnp: Use list_for_each_entry() instead of open coding hwmon: (da9052) Synchronize access with mfd IB/mlx4: Test return value of calls to ib_get_cached_pkey netfilter: conntrack: avoid gcc-10 zero-length-bounds warning i40iw: Fix error handling in i40iw_manage_arp_cache() pinctrl: cherryview: Add missing spinlock usage in chv_gpio_irq_handler pinctrl: baytrail: Enable pin configuration setting for GPIO chip ipmi: Fix NULL pointer dereference in ssif_probe x86/entry/64: Fix unwind hints in register clearing code ALSA: hda/realtek - Fix S3 pop noise on Dell Wyse ipc/util.c: sysvipc_find_ipc() incorrectly updates position index drm/qxl: lost qxl_bo_kunmap_atomic_page in qxl_image_init_helper() ALSA: hda/hdmi: fix race in monitor detection during probe cpufreq: intel_pstate: Only mention the BIOS disabling turbo mode once dmaengine: mmp_tdma: Reset channel error on release dmaengine: pch_dma.c: Avoid data race between probe and irq handler scsi: sg: add sg_remove_request in sg_write virtio-blk: handle block_device_operations callbacks after hot unplug drop_monitor: work around gcc-10 stringop-overflow warning net: moxa: Fix a potential double 'free_irq()' net/sonic: Fix a resource leak in an error handling path in 'jazz_sonic_probe()' shmem: fix possible deadlocks on shmlock_user_lock net: stmmac: Use mutex instead of spinlock f2fs: fix to avoid memory leakage in f2fs_listxattr f2fs: fix to avoid accessing xattr across the boundary f2fs: sanity check of xattr entry size f2fs: introduce read_xattr_block f2fs: introduce read_inline_xattr blktrace: fix dereference after null check blktrace: Protect q->blk_trace with RCU blktrace: fix trace mutex deadlock blktrace: fix unlocked access to init/start-stop/teardown net: ipv6_stub: use ip6_dst_lookup_flow instead of ip6_dst_lookup net: ipv6: add net argument to ip6_dst_lookup_flow scripts/decodecode: fix trapping instruction formatting objtool: Fix stack offset tracking for indirect CFAs netfilter: nat: never update the UDP checksum when it's 0 x86/unwind/orc: Fix error path for bad ORC entry type x86/unwind/orc: Prevent unwinding before ORC initialization x86/unwind/orc: Don't skip the first frame for inactive tasks x86/entry/64: Fix unwind hints in rewind_stack_do_exit() x86/entry/64: Fix unwind hints in kernel exit path batman-adv: Fix refcnt leak in batadv_v_ogm_process batman-adv: Fix refcnt leak in batadv_store_throughput_override batman-adv: Fix refcnt leak in batadv_show_throughput_override batman-adv: fix batadv_nc_random_weight_tq coredump: fix crash when umh is disabled mm/page_alloc: fix watchdog soft lockups during set_zone_contiguous() KVM: arm: vgic: Fix limit condition when writing to GICD_I[CS]ACTIVER tracing: Add a vmalloc_sync_mappings() for safe measure USB: serial: garmin_gps: add sanity checking for data length USB: uas: add quirk for LaCie 2Big Quadra HID: usbhid: Fix race between usbhid_close() and usbhid_stop() geneve: only configure or fill UDP_ZERO_CSUM6_RX/TX info when CONFIG_IPV6 HID: wacom: Read HID_DG_CONTACTMAX directly for non-generic devices ipv6: fix cleanup ordering for ip6_mr failure net: stricter validation of untrusted gso packets bnxt_en: Fix VF anti-spoof filter setup. bnxt_en: Improve AER slot reset. net/mlx5: Fix command entry leak in Internal Error State net/mlx5: Fix forced completion access non initialized command entry bnxt_en: Fix VLAN acceleration handling in bnxt_fix_features(). sch_sfq: validate silly quantum values sch_choke: avoid potential panic in choke_reset() net: usb: qmi_wwan: add support for DW5816e net/mlx4_core: Fix use of ENOSPC around mlx4_counter_alloc() net: macsec: preserve ingress frame ordering fq_codel: fix TCA_FQ_CODEL_DROP_BATCH_SIZE sanity checks dp83640: reverse arguments to list_add_tail USB: serial: qcserial: Add DW5816e support f2fs: compress: fix zstd data corruption f2fs: add compressed/gc data read IO stat f2fs: fix potential use-after-free issue f2fs: compress: don't handle non-compressed data in workqueue f2fs: remove redundant assignment to variable err f2fs: refactor resize_fs to avoid meta updates in progress f2fs: use round_up to enhance calculation f2fs: introduce F2FS_IOC_RESERVE_COMPRESS_BLOCKS f2fs: Avoid double lock for cp_rwsem during checkpoint f2fs: report delalloc reserve as non-free in statfs for project quota f2fs: Fix wrong stub helper update_sit_info f2fs: compress: let lz4 compressor handle output buffer budget properly f2fs: remove blk_plugging in block_operations f2fs: introduce F2FS_IOC_RELEASE_COMPRESS_BLOCKS f2fs: shrink spinlock coverage f2fs: correctly fix the parent inode number during fsync() f2fs: introduce mempool for {,de}compress intermediate page allocation f2fs: introduce f2fs_bmap_compress() f2fs: support fiemap on compressed inode f2fs: support partial truncation on compressed inode f2fs: remove redundant compress inode check f2fs: flush dirty meta pages when flushing them f2fs: use strcmp() in parse_options() f2fs: fix checkpoint=disable:%u%% f2fs: Use the correct style for SPDX License Identifier f2fs: rework filename handling f2fs: split f2fs_d_compare() from f2fs_match_name() f2fs: don't leak filename in f2fs_try_convert_inline_dir() ANDROID: clang: update to 11.0.1 FROMLIST: x86_64: fix jiffies ODR violation ANDROID: cuttlefish_defconfig: Enable net testing options ANDROID: Incremental fs: wake up log pollers less often ANDROID: Incremental fs: Fix scheduling while atomic error ANDROID: Incremental fs: Avoid continually recalculating hashes Revert "f2fs: refactor resize_fs to avoid meta updates in progress" UPSTREAM: HID: steam: Fix input device disappearing ANDROID: fscrypt: set dun_bytes more precisely ANDROID: dm-default-key: set dun_bytes more precisely ANDROID: block: backport the ability to specify max_dun_bytes ANDROID: hid: steam: remove BT controller matching ANDROID: dm-default-key: Update key size for wrapped keys ANDROID: cuttlefish_defconfig: Enable CONFIG_STATIC_USERMODEHELPER ANDROID: cuttlefish_defconfig: enable CONFIG_MMC_CRYPTO ANDROID: Add padding for crypto related structs in UFS and MMC ANDROID: mmc: MMC crypto API f2fs: fix missing check for f2fs_unlock_op f2fs: refactor resize_fs to avoid meta updates in progress Conflicts: Documentation/devicetree/bindings/usb/dwc3.txt drivers/block/virtio_blk.c drivers/mmc/core/Kconfig drivers/mmc/core/block.c drivers/mmc/host/sdhci-msm.c drivers/net/ethernet/stmicro/stmmac/stmmac.h drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c drivers/net/ethernet/stmicro/stmmac/stmmac_main.c drivers/scsi/ufs/ufs-qcom.c drivers/usb/gadget/composite.c drivers/usb/gadget/function/f_uac1_legacy.c fs/crypto/crypto.c fs/crypto/inline_crypt.c fs/crypto/keyring.c fs/f2fs/checkpoint.c include/linux/fs.h include/linux/mmc/host.h include/linux/mod_devicetable.h include/uapi/linux/input-event-codes.h net/qrtr/qrtr.c sound/core/compress_offload.c sound/core/rawmidi.c Fixed build errors: drivers/scsi/ufs/ufshcd.c Change-Id: I2add911b58d3c87b666ffa0fe46cbceb6cc56430 Signed-off-by: Srinivasarao P <spathi@codeaurora.org>
3892 lines
94 KiB
C
3892 lines
94 KiB
C
/*
|
|
* drivers/net/macsec.c - MACsec device
|
|
*
|
|
* Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net>
|
|
*
|
|
* 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
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/module.h>
|
|
#include <crypto/aead.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <net/genetlink.h>
|
|
#include <net/sock.h>
|
|
#include <net/gro_cells.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <uapi/linux/if_macsec.h>
|
|
|
|
#define MACSEC_SCI_LEN 8
|
|
|
|
/* SecTAG length = macsec_eth_header without the optional SCI */
|
|
#define MACSEC_TAG_LEN 6
|
|
|
|
struct macsec_eth_header {
|
|
struct ethhdr eth;
|
|
/* SecTAG */
|
|
u8 tci_an;
|
|
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
|
u8 short_length:6,
|
|
unused:2;
|
|
#elif defined(__BIG_ENDIAN_BITFIELD)
|
|
u8 unused:2,
|
|
short_length:6;
|
|
#else
|
|
#error "Please fix <asm/byteorder.h>"
|
|
#endif
|
|
__be32 packet_number;
|
|
u8 secure_channel_id[8]; /* optional */
|
|
} __packed;
|
|
|
|
#define MACSEC_TCI_VERSION 0x80
|
|
#define MACSEC_TCI_ES 0x40 /* end station */
|
|
#define MACSEC_TCI_SC 0x20 /* SCI present */
|
|
#define MACSEC_TCI_SCB 0x10 /* epon */
|
|
#define MACSEC_TCI_E 0x08 /* encryption */
|
|
#define MACSEC_TCI_C 0x04 /* changed text */
|
|
#define MACSEC_AN_MASK 0x03 /* association number */
|
|
#define MACSEC_TCI_CONFID (MACSEC_TCI_E | MACSEC_TCI_C)
|
|
|
|
/* minimum secure data length deemed "not short", see IEEE 802.1AE-2006 9.7 */
|
|
#define MIN_NON_SHORT_LEN 48
|
|
|
|
#define GCM_AES_IV_LEN 12
|
|
#define DEFAULT_ICV_LEN 16
|
|
|
|
#define for_each_rxsc(secy, sc) \
|
|
for (sc = rcu_dereference_bh(secy->rx_sc); \
|
|
sc; \
|
|
sc = rcu_dereference_bh(sc->next))
|
|
#define for_each_rxsc_rtnl(secy, sc) \
|
|
for (sc = rtnl_dereference(secy->rx_sc); \
|
|
sc; \
|
|
sc = rtnl_dereference(sc->next))
|
|
|
|
struct gcm_iv {
|
|
union {
|
|
u8 secure_channel_id[8];
|
|
sci_t sci;
|
|
};
|
|
__be32 pn;
|
|
};
|
|
|
|
#define MACSEC_VALIDATE_DEFAULT MACSEC_VALIDATE_STRICT
|
|
|
|
struct pcpu_secy_stats {
|
|
struct macsec_dev_stats stats;
|
|
struct u64_stats_sync syncp;
|
|
};
|
|
|
|
/**
|
|
* struct macsec_dev - private data
|
|
* @secy: SecY config
|
|
* @real_dev: pointer to underlying netdevice
|
|
* @stats: MACsec device stats
|
|
* @secys: linked list of SecY's on the underlying device
|
|
*/
|
|
struct macsec_dev {
|
|
struct macsec_secy secy;
|
|
struct net_device *real_dev;
|
|
struct pcpu_secy_stats __percpu *stats;
|
|
struct list_head secys;
|
|
struct gro_cells gro_cells;
|
|
unsigned int nest_level;
|
|
};
|
|
|
|
/**
|
|
* struct macsec_rxh_data - rx_handler private argument
|
|
* @secys: linked list of SecY's on this underlying device
|
|
*/
|
|
struct macsec_rxh_data {
|
|
struct list_head secys;
|
|
};
|
|
|
|
static struct macsec_dev *macsec_priv(const struct net_device *dev)
|
|
{
|
|
return (struct macsec_dev *)netdev_priv(dev);
|
|
}
|
|
|
|
static struct macsec_rxh_data *macsec_data_rcu(const struct net_device *dev)
|
|
{
|
|
return rcu_dereference_bh(dev->rx_handler_data);
|
|
}
|
|
|
|
static struct macsec_rxh_data *macsec_data_rtnl(const struct net_device *dev)
|
|
{
|
|
return rtnl_dereference(dev->rx_handler_data);
|
|
}
|
|
|
|
struct macsec_cb {
|
|
struct aead_request *req;
|
|
union {
|
|
struct macsec_tx_sa *tx_sa;
|
|
struct macsec_rx_sa *rx_sa;
|
|
};
|
|
u8 assoc_num;
|
|
bool valid;
|
|
bool has_sci;
|
|
};
|
|
|
|
static struct macsec_rx_sa *macsec_rxsa_get(struct macsec_rx_sa __rcu *ptr)
|
|
{
|
|
struct macsec_rx_sa *sa = rcu_dereference_bh(ptr);
|
|
|
|
if (!sa || !sa->active)
|
|
return NULL;
|
|
|
|
if (!atomic_inc_not_zero(&sa->refcnt))
|
|
return NULL;
|
|
|
|
return sa;
|
|
}
|
|
|
|
static void free_rx_sc_rcu(struct rcu_head *head)
|
|
{
|
|
struct macsec_rx_sc *rx_sc = container_of(head, struct macsec_rx_sc, rcu_head);
|
|
|
|
free_percpu(rx_sc->stats);
|
|
kfree(rx_sc);
|
|
}
|
|
|
|
static struct macsec_rx_sc *macsec_rxsc_get(struct macsec_rx_sc *sc)
|
|
{
|
|
return atomic_inc_not_zero(&sc->refcnt) ? sc : NULL;
|
|
}
|
|
|
|
static void macsec_rxsc_put(struct macsec_rx_sc *sc)
|
|
{
|
|
if (atomic_dec_and_test(&sc->refcnt))
|
|
call_rcu(&sc->rcu_head, free_rx_sc_rcu);
|
|
}
|
|
|
|
static void free_rxsa(struct rcu_head *head)
|
|
{
|
|
struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu);
|
|
|
|
crypto_free_aead(sa->key.tfm);
|
|
free_percpu(sa->stats);
|
|
kfree(sa);
|
|
}
|
|
|
|
static void macsec_rxsa_put(struct macsec_rx_sa *sa)
|
|
{
|
|
if (atomic_dec_and_test(&sa->refcnt))
|
|
call_rcu(&sa->rcu, free_rxsa);
|
|
}
|
|
|
|
static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr)
|
|
{
|
|
struct macsec_tx_sa *sa = rcu_dereference_bh(ptr);
|
|
|
|
if (!sa || !sa->active)
|
|
return NULL;
|
|
|
|
if (!atomic_inc_not_zero(&sa->refcnt))
|
|
return NULL;
|
|
|
|
return sa;
|
|
}
|
|
|
|
static void free_txsa(struct rcu_head *head)
|
|
{
|
|
struct macsec_tx_sa *sa = container_of(head, struct macsec_tx_sa, rcu);
|
|
|
|
crypto_free_aead(sa->key.tfm);
|
|
free_percpu(sa->stats);
|
|
kfree(sa);
|
|
}
|
|
|
|
static void macsec_txsa_put(struct macsec_tx_sa *sa)
|
|
{
|
|
if (atomic_dec_and_test(&sa->refcnt))
|
|
call_rcu(&sa->rcu, free_txsa);
|
|
}
|
|
|
|
static struct macsec_cb *macsec_skb_cb(struct sk_buff *skb)
|
|
{
|
|
BUILD_BUG_ON(sizeof(struct macsec_cb) > sizeof(skb->cb));
|
|
return (struct macsec_cb *)skb->cb;
|
|
}
|
|
|
|
#define MACSEC_PORT_ES (htons(0x0001))
|
|
#define MACSEC_PORT_SCB (0x0000)
|
|
#define MACSEC_UNDEF_SCI ((__force sci_t)0xffffffffffffffffULL)
|
|
|
|
#define DEFAULT_SAK_LEN 16
|
|
#define DEFAULT_SEND_SCI true
|
|
#define DEFAULT_ENCRYPT false
|
|
#define DEFAULT_ENCODING_SA 0
|
|
|
|
static bool send_sci(const struct macsec_secy *secy)
|
|
{
|
|
const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
|
|
|
|
return tx_sc->send_sci ||
|
|
(secy->n_rx_sc > 1 && !tx_sc->end_station && !tx_sc->scb);
|
|
}
|
|
|
|
static sci_t make_sci(u8 *addr, __be16 port)
|
|
{
|
|
sci_t sci;
|
|
|
|
memcpy(&sci, addr, ETH_ALEN);
|
|
memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port));
|
|
|
|
return sci;
|
|
}
|
|
|
|
static sci_t macsec_frame_sci(struct macsec_eth_header *hdr, bool sci_present)
|
|
{
|
|
sci_t sci;
|
|
|
|
if (sci_present)
|
|
memcpy(&sci, hdr->secure_channel_id,
|
|
sizeof(hdr->secure_channel_id));
|
|
else
|
|
sci = make_sci(hdr->eth.h_source, MACSEC_PORT_ES);
|
|
|
|
return sci;
|
|
}
|
|
|
|
static unsigned int macsec_sectag_len(bool sci_present)
|
|
{
|
|
return MACSEC_TAG_LEN + (sci_present ? MACSEC_SCI_LEN : 0);
|
|
}
|
|
|
|
static unsigned int macsec_hdr_len(bool sci_present)
|
|
{
|
|
return macsec_sectag_len(sci_present) + ETH_HLEN;
|
|
}
|
|
|
|
static unsigned int macsec_extra_len(bool sci_present)
|
|
{
|
|
return macsec_sectag_len(sci_present) + sizeof(__be16);
|
|
}
|
|
|
|
/* Fill SecTAG according to IEEE 802.1AE-2006 10.5.3 */
|
|
static void macsec_fill_sectag(struct macsec_eth_header *h,
|
|
const struct macsec_secy *secy, u32 pn,
|
|
bool sci_present)
|
|
{
|
|
const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
|
|
|
|
memset(&h->tci_an, 0, macsec_sectag_len(sci_present));
|
|
h->eth.h_proto = htons(ETH_P_MACSEC);
|
|
|
|
if (sci_present) {
|
|
h->tci_an |= MACSEC_TCI_SC;
|
|
memcpy(&h->secure_channel_id, &secy->sci,
|
|
sizeof(h->secure_channel_id));
|
|
} else {
|
|
if (tx_sc->end_station)
|
|
h->tci_an |= MACSEC_TCI_ES;
|
|
if (tx_sc->scb)
|
|
h->tci_an |= MACSEC_TCI_SCB;
|
|
}
|
|
|
|
h->packet_number = htonl(pn);
|
|
|
|
/* with GCM, C/E clear for !encrypt, both set for encrypt */
|
|
if (tx_sc->encrypt)
|
|
h->tci_an |= MACSEC_TCI_CONFID;
|
|
else if (secy->icv_len != DEFAULT_ICV_LEN)
|
|
h->tci_an |= MACSEC_TCI_C;
|
|
|
|
h->tci_an |= tx_sc->encoding_sa;
|
|
}
|
|
|
|
static void macsec_set_shortlen(struct macsec_eth_header *h, size_t data_len)
|
|
{
|
|
if (data_len < MIN_NON_SHORT_LEN)
|
|
h->short_length = data_len;
|
|
}
|
|
|
|
/* Checks if underlying layers implement MACsec offloading functions
|
|
* and returns a pointer to the MACsec ops struct if any (also updates
|
|
* the MACsec context device reference if provided).
|
|
*/
|
|
static const struct macsec_ops *macsec_get_ops(struct macsec_dev *dev,
|
|
struct macsec_context *ctx)
|
|
{
|
|
struct phy_device *phydev;
|
|
|
|
if (!dev || !dev->real_dev)
|
|
return NULL;
|
|
|
|
/* Check if the PHY device provides MACsec ops */
|
|
phydev = dev->real_dev->phydev;
|
|
if (phydev && phydev->macsec_ops) {
|
|
if (ctx) {
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->phydev = phydev;
|
|
ctx->is_phy = 1;
|
|
}
|
|
|
|
return phydev->macsec_ops;
|
|
}
|
|
|
|
/* Check if the net device provides MACsec ops */
|
|
if (dev->real_dev->features & NETIF_F_HW_MACSEC &&
|
|
dev->real_dev->macsec_ops) {
|
|
if (ctx) {
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->netdev = dev->real_dev;
|
|
}
|
|
|
|
return dev->real_dev->macsec_ops;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* validate MACsec packet according to IEEE 802.1AE-2006 9.12 */
|
|
static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len)
|
|
{
|
|
struct macsec_eth_header *h = (struct macsec_eth_header *)skb->data;
|
|
int len = skb->len - 2 * ETH_ALEN;
|
|
int extra_len = macsec_extra_len(!!(h->tci_an & MACSEC_TCI_SC)) + icv_len;
|
|
|
|
/* a) It comprises at least 17 octets */
|
|
if (skb->len <= 16)
|
|
return false;
|
|
|
|
/* b) MACsec EtherType: already checked */
|
|
|
|
/* c) V bit is clear */
|
|
if (h->tci_an & MACSEC_TCI_VERSION)
|
|
return false;
|
|
|
|
/* d) ES or SCB => !SC */
|
|
if ((h->tci_an & MACSEC_TCI_ES || h->tci_an & MACSEC_TCI_SCB) &&
|
|
(h->tci_an & MACSEC_TCI_SC))
|
|
return false;
|
|
|
|
/* e) Bits 7 and 8 of octet 4 of the SecTAG are clear */
|
|
if (h->unused)
|
|
return false;
|
|
|
|
/* rx.pn != 0 (figure 10-5) */
|
|
if (!h->packet_number)
|
|
return false;
|
|
|
|
/* length check, f) g) h) i) */
|
|
if (h->short_length)
|
|
return len == extra_len + h->short_length;
|
|
return len >= extra_len + MIN_NON_SHORT_LEN;
|
|
}
|
|
|
|
#define MACSEC_NEEDED_HEADROOM (macsec_extra_len(true))
|
|
#define MACSEC_NEEDED_TAILROOM MACSEC_STD_ICV_LEN
|
|
|
|
static void macsec_fill_iv(unsigned char *iv, sci_t sci, u32 pn)
|
|
{
|
|
struct gcm_iv *gcm_iv = (struct gcm_iv *)iv;
|
|
|
|
gcm_iv->sci = sci;
|
|
gcm_iv->pn = htonl(pn);
|
|
}
|
|
|
|
static struct macsec_eth_header *macsec_ethhdr(struct sk_buff *skb)
|
|
{
|
|
return (struct macsec_eth_header *)skb_mac_header(skb);
|
|
}
|
|
|
|
static sci_t dev_to_sci(struct net_device *dev, __be16 port)
|
|
{
|
|
return make_sci(dev->dev_addr, port);
|
|
}
|
|
|
|
static void __macsec_pn_wrapped(struct macsec_secy *secy,
|
|
struct macsec_tx_sa *tx_sa)
|
|
{
|
|
pr_debug("PN wrapped, transitioning to !oper\n");
|
|
tx_sa->active = false;
|
|
if (secy->protect_frames)
|
|
secy->operational = false;
|
|
}
|
|
|
|
void macsec_pn_wrapped(struct macsec_secy *secy, struct macsec_tx_sa *tx_sa)
|
|
{
|
|
spin_lock_bh(&tx_sa->lock);
|
|
__macsec_pn_wrapped(secy, tx_sa);
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(macsec_pn_wrapped);
|
|
|
|
static u32 tx_sa_update_pn(struct macsec_tx_sa *tx_sa, struct macsec_secy *secy)
|
|
{
|
|
u32 pn;
|
|
|
|
spin_lock_bh(&tx_sa->lock);
|
|
pn = tx_sa->next_pn;
|
|
|
|
tx_sa->next_pn++;
|
|
if (tx_sa->next_pn == 0)
|
|
__macsec_pn_wrapped(secy, tx_sa);
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
|
|
return pn;
|
|
}
|
|
|
|
static void macsec_encrypt_finish(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = netdev_priv(dev);
|
|
|
|
skb->dev = macsec->real_dev;
|
|
skb_reset_mac_header(skb);
|
|
skb->protocol = eth_hdr(skb)->h_proto;
|
|
}
|
|
|
|
static void macsec_count_tx(struct sk_buff *skb, struct macsec_tx_sc *tx_sc,
|
|
struct macsec_tx_sa *tx_sa)
|
|
{
|
|
struct pcpu_tx_sc_stats *txsc_stats = this_cpu_ptr(tx_sc->stats);
|
|
|
|
u64_stats_update_begin(&txsc_stats->syncp);
|
|
if (tx_sc->encrypt) {
|
|
txsc_stats->stats.OutOctetsEncrypted += skb->len;
|
|
txsc_stats->stats.OutPktsEncrypted++;
|
|
this_cpu_inc(tx_sa->stats->OutPktsEncrypted);
|
|
} else {
|
|
txsc_stats->stats.OutOctetsProtected += skb->len;
|
|
txsc_stats->stats.OutPktsProtected++;
|
|
this_cpu_inc(tx_sa->stats->OutPktsProtected);
|
|
}
|
|
u64_stats_update_end(&txsc_stats->syncp);
|
|
}
|
|
|
|
static void count_tx(struct net_device *dev, int ret, int len)
|
|
{
|
|
if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
|
|
struct pcpu_sw_netstats *stats = this_cpu_ptr(dev->tstats);
|
|
|
|
u64_stats_update_begin(&stats->syncp);
|
|
stats->tx_packets++;
|
|
stats->tx_bytes += len;
|
|
u64_stats_update_end(&stats->syncp);
|
|
}
|
|
}
|
|
|
|
static void macsec_encrypt_done(struct crypto_async_request *base, int err)
|
|
{
|
|
struct sk_buff *skb = base->data;
|
|
struct net_device *dev = skb->dev;
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct macsec_tx_sa *sa = macsec_skb_cb(skb)->tx_sa;
|
|
int len, ret;
|
|
|
|
aead_request_free(macsec_skb_cb(skb)->req);
|
|
|
|
rcu_read_lock_bh();
|
|
macsec_encrypt_finish(skb, dev);
|
|
macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa);
|
|
len = skb->len;
|
|
ret = dev_queue_xmit(skb);
|
|
count_tx(dev, ret, len);
|
|
rcu_read_unlock_bh();
|
|
|
|
macsec_txsa_put(sa);
|
|
dev_put(dev);
|
|
}
|
|
|
|
static struct aead_request *macsec_alloc_req(struct crypto_aead *tfm,
|
|
unsigned char **iv,
|
|
struct scatterlist **sg,
|
|
int num_frags)
|
|
{
|
|
size_t size, iv_offset, sg_offset;
|
|
struct aead_request *req;
|
|
void *tmp;
|
|
|
|
size = sizeof(struct aead_request) + crypto_aead_reqsize(tfm);
|
|
iv_offset = size;
|
|
size += GCM_AES_IV_LEN;
|
|
|
|
size = ALIGN(size, __alignof__(struct scatterlist));
|
|
sg_offset = size;
|
|
size += sizeof(struct scatterlist) * num_frags;
|
|
|
|
tmp = kmalloc(size, GFP_ATOMIC);
|
|
if (!tmp)
|
|
return NULL;
|
|
|
|
*iv = (unsigned char *)(tmp + iv_offset);
|
|
*sg = (struct scatterlist *)(tmp + sg_offset);
|
|
req = tmp;
|
|
|
|
aead_request_set_tfm(req, tfm);
|
|
|
|
return req;
|
|
}
|
|
|
|
static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
int ret;
|
|
struct scatterlist *sg;
|
|
struct sk_buff *trailer;
|
|
unsigned char *iv;
|
|
struct ethhdr *eth;
|
|
struct macsec_eth_header *hh;
|
|
size_t unprotected_len;
|
|
struct aead_request *req;
|
|
struct macsec_secy *secy;
|
|
struct macsec_tx_sc *tx_sc;
|
|
struct macsec_tx_sa *tx_sa;
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
bool sci_present;
|
|
u32 pn;
|
|
|
|
secy = &macsec->secy;
|
|
tx_sc = &secy->tx_sc;
|
|
|
|
/* 10.5.1 TX SA assignment */
|
|
tx_sa = macsec_txsa_get(tx_sc->sa[tx_sc->encoding_sa]);
|
|
if (!tx_sa) {
|
|
secy->operational = false;
|
|
kfree_skb(skb);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
if (unlikely(skb_headroom(skb) < MACSEC_NEEDED_HEADROOM ||
|
|
skb_tailroom(skb) < MACSEC_NEEDED_TAILROOM)) {
|
|
struct sk_buff *nskb = skb_copy_expand(skb,
|
|
MACSEC_NEEDED_HEADROOM,
|
|
MACSEC_NEEDED_TAILROOM,
|
|
GFP_ATOMIC);
|
|
if (likely(nskb)) {
|
|
consume_skb(skb);
|
|
skb = nskb;
|
|
} else {
|
|
macsec_txsa_put(tx_sa);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
} else {
|
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
|
if (!skb) {
|
|
macsec_txsa_put(tx_sa);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
}
|
|
|
|
unprotected_len = skb->len;
|
|
eth = eth_hdr(skb);
|
|
sci_present = send_sci(secy);
|
|
hh = skb_push(skb, macsec_extra_len(sci_present));
|
|
memmove(hh, eth, 2 * ETH_ALEN);
|
|
|
|
pn = tx_sa_update_pn(tx_sa, secy);
|
|
if (pn == 0) {
|
|
macsec_txsa_put(tx_sa);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(-ENOLINK);
|
|
}
|
|
macsec_fill_sectag(hh, secy, pn, sci_present);
|
|
macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN);
|
|
|
|
skb_put(skb, secy->icv_len);
|
|
|
|
if (skb->len - ETH_HLEN > macsec_priv(dev)->real_dev->mtu) {
|
|
struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats);
|
|
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.OutPktsTooLong++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
|
|
macsec_txsa_put(tx_sa);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
ret = skb_cow_data(skb, 0, &trailer);
|
|
if (unlikely(ret < 0)) {
|
|
macsec_txsa_put(tx_sa);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
req = macsec_alloc_req(tx_sa->key.tfm, &iv, &sg, ret);
|
|
if (!req) {
|
|
macsec_txsa_put(tx_sa);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
macsec_fill_iv(iv, secy->sci, pn);
|
|
|
|
sg_init_table(sg, ret);
|
|
ret = skb_to_sgvec(skb, sg, 0, skb->len);
|
|
if (unlikely(ret < 0)) {
|
|
aead_request_free(req);
|
|
macsec_txsa_put(tx_sa);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
if (tx_sc->encrypt) {
|
|
int len = skb->len - macsec_hdr_len(sci_present) -
|
|
secy->icv_len;
|
|
aead_request_set_crypt(req, sg, sg, len, iv);
|
|
aead_request_set_ad(req, macsec_hdr_len(sci_present));
|
|
} else {
|
|
aead_request_set_crypt(req, sg, sg, 0, iv);
|
|
aead_request_set_ad(req, skb->len - secy->icv_len);
|
|
}
|
|
|
|
macsec_skb_cb(skb)->req = req;
|
|
macsec_skb_cb(skb)->tx_sa = tx_sa;
|
|
aead_request_set_callback(req, 0, macsec_encrypt_done, skb);
|
|
|
|
dev_hold(skb->dev);
|
|
ret = crypto_aead_encrypt(req);
|
|
if (ret == -EINPROGRESS) {
|
|
return ERR_PTR(ret);
|
|
} else if (ret != 0) {
|
|
dev_put(skb->dev);
|
|
kfree_skb(skb);
|
|
aead_request_free(req);
|
|
macsec_txsa_put(tx_sa);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
dev_put(skb->dev);
|
|
aead_request_free(req);
|
|
macsec_txsa_put(tx_sa);
|
|
|
|
return skb;
|
|
}
|
|
|
|
static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u32 pn)
|
|
{
|
|
struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa;
|
|
struct pcpu_rx_sc_stats *rxsc_stats = this_cpu_ptr(rx_sa->sc->stats);
|
|
struct macsec_eth_header *hdr = macsec_ethhdr(skb);
|
|
u32 lowest_pn = 0;
|
|
|
|
spin_lock(&rx_sa->lock);
|
|
if (rx_sa->next_pn >= secy->replay_window)
|
|
lowest_pn = rx_sa->next_pn - secy->replay_window;
|
|
|
|
/* Now perform replay protection check again
|
|
* (see IEEE 802.1AE-2006 figure 10-5)
|
|
*/
|
|
if (secy->replay_protect && pn < lowest_pn) {
|
|
spin_unlock(&rx_sa->lock);
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
rxsc_stats->stats.InPktsLate++;
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
return false;
|
|
}
|
|
|
|
if (secy->validate_frames != MACSEC_VALIDATE_DISABLED) {
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
if (hdr->tci_an & MACSEC_TCI_E)
|
|
rxsc_stats->stats.InOctetsDecrypted += skb->len;
|
|
else
|
|
rxsc_stats->stats.InOctetsValidated += skb->len;
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
}
|
|
|
|
if (!macsec_skb_cb(skb)->valid) {
|
|
spin_unlock(&rx_sa->lock);
|
|
|
|
/* 10.6.5 */
|
|
if (hdr->tci_an & MACSEC_TCI_C ||
|
|
secy->validate_frames == MACSEC_VALIDATE_STRICT) {
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
rxsc_stats->stats.InPktsNotValid++;
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
return false;
|
|
}
|
|
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
if (secy->validate_frames == MACSEC_VALIDATE_CHECK) {
|
|
rxsc_stats->stats.InPktsInvalid++;
|
|
this_cpu_inc(rx_sa->stats->InPktsInvalid);
|
|
} else if (pn < lowest_pn) {
|
|
rxsc_stats->stats.InPktsDelayed++;
|
|
} else {
|
|
rxsc_stats->stats.InPktsUnchecked++;
|
|
}
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
} else {
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
if (pn < lowest_pn) {
|
|
rxsc_stats->stats.InPktsDelayed++;
|
|
} else {
|
|
rxsc_stats->stats.InPktsOK++;
|
|
this_cpu_inc(rx_sa->stats->InPktsOK);
|
|
}
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
|
|
if (pn >= rx_sa->next_pn)
|
|
rx_sa->next_pn = pn + 1;
|
|
spin_unlock(&rx_sa->lock);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void macsec_reset_skb(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
skb->pkt_type = PACKET_HOST;
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
|
|
skb_reset_network_header(skb);
|
|
if (!skb_transport_header_was_set(skb))
|
|
skb_reset_transport_header(skb);
|
|
skb_reset_mac_len(skb);
|
|
}
|
|
|
|
static void macsec_finalize_skb(struct sk_buff *skb, u8 icv_len, u8 hdr_len)
|
|
{
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
memmove(skb->data + hdr_len, skb->data, 2 * ETH_ALEN);
|
|
skb_pull(skb, hdr_len);
|
|
pskb_trim_unique(skb, skb->len - icv_len);
|
|
}
|
|
|
|
static void count_rx(struct net_device *dev, int len)
|
|
{
|
|
struct pcpu_sw_netstats *stats = this_cpu_ptr(dev->tstats);
|
|
|
|
u64_stats_update_begin(&stats->syncp);
|
|
stats->rx_packets++;
|
|
stats->rx_bytes += len;
|
|
u64_stats_update_end(&stats->syncp);
|
|
}
|
|
|
|
static void macsec_decrypt_done(struct crypto_async_request *base, int err)
|
|
{
|
|
struct sk_buff *skb = base->data;
|
|
struct net_device *dev = skb->dev;
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa;
|
|
struct macsec_rx_sc *rx_sc = rx_sa->sc;
|
|
int len;
|
|
u32 pn;
|
|
|
|
aead_request_free(macsec_skb_cb(skb)->req);
|
|
|
|
if (!err)
|
|
macsec_skb_cb(skb)->valid = true;
|
|
|
|
rcu_read_lock_bh();
|
|
pn = ntohl(macsec_ethhdr(skb)->packet_number);
|
|
if (!macsec_post_decrypt(skb, &macsec->secy, pn)) {
|
|
rcu_read_unlock_bh();
|
|
kfree_skb(skb);
|
|
goto out;
|
|
}
|
|
|
|
macsec_finalize_skb(skb, macsec->secy.icv_len,
|
|
macsec_extra_len(macsec_skb_cb(skb)->has_sci));
|
|
macsec_reset_skb(skb, macsec->secy.netdev);
|
|
|
|
len = skb->len;
|
|
if (gro_cells_receive(&macsec->gro_cells, skb) == NET_RX_SUCCESS)
|
|
count_rx(dev, len);
|
|
|
|
rcu_read_unlock_bh();
|
|
|
|
out:
|
|
macsec_rxsa_put(rx_sa);
|
|
macsec_rxsc_put(rx_sc);
|
|
dev_put(dev);
|
|
}
|
|
|
|
static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
|
|
struct net_device *dev,
|
|
struct macsec_rx_sa *rx_sa,
|
|
sci_t sci,
|
|
struct macsec_secy *secy)
|
|
{
|
|
int ret;
|
|
struct scatterlist *sg;
|
|
struct sk_buff *trailer;
|
|
unsigned char *iv;
|
|
struct aead_request *req;
|
|
struct macsec_eth_header *hdr;
|
|
u16 icv_len = secy->icv_len;
|
|
|
|
macsec_skb_cb(skb)->valid = false;
|
|
skb = skb_share_check(skb, GFP_ATOMIC);
|
|
if (!skb)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
ret = skb_cow_data(skb, 0, &trailer);
|
|
if (unlikely(ret < 0)) {
|
|
kfree_skb(skb);
|
|
return ERR_PTR(ret);
|
|
}
|
|
req = macsec_alloc_req(rx_sa->key.tfm, &iv, &sg, ret);
|
|
if (!req) {
|
|
kfree_skb(skb);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
hdr = (struct macsec_eth_header *)skb->data;
|
|
macsec_fill_iv(iv, sci, ntohl(hdr->packet_number));
|
|
|
|
sg_init_table(sg, ret);
|
|
ret = skb_to_sgvec(skb, sg, 0, skb->len);
|
|
if (unlikely(ret < 0)) {
|
|
aead_request_free(req);
|
|
kfree_skb(skb);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
if (hdr->tci_an & MACSEC_TCI_E) {
|
|
/* confidentiality: ethernet + macsec header
|
|
* authenticated, encrypted payload
|
|
*/
|
|
int len = skb->len - macsec_hdr_len(macsec_skb_cb(skb)->has_sci);
|
|
|
|
aead_request_set_crypt(req, sg, sg, len, iv);
|
|
aead_request_set_ad(req, macsec_hdr_len(macsec_skb_cb(skb)->has_sci));
|
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
|
if (!skb) {
|
|
aead_request_free(req);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
} else {
|
|
/* integrity only: all headers + data authenticated */
|
|
aead_request_set_crypt(req, sg, sg, icv_len, iv);
|
|
aead_request_set_ad(req, skb->len - icv_len);
|
|
}
|
|
|
|
macsec_skb_cb(skb)->req = req;
|
|
skb->dev = dev;
|
|
aead_request_set_callback(req, 0, macsec_decrypt_done, skb);
|
|
|
|
dev_hold(dev);
|
|
ret = crypto_aead_decrypt(req);
|
|
if (ret == -EINPROGRESS) {
|
|
return ERR_PTR(ret);
|
|
} else if (ret != 0) {
|
|
/* decryption/authentication failed
|
|
* 10.6 if validateFrames is disabled, deliver anyway
|
|
*/
|
|
if (ret != -EBADMSG) {
|
|
kfree_skb(skb);
|
|
skb = ERR_PTR(ret);
|
|
}
|
|
} else {
|
|
macsec_skb_cb(skb)->valid = true;
|
|
}
|
|
dev_put(dev);
|
|
|
|
aead_request_free(req);
|
|
|
|
return skb;
|
|
}
|
|
|
|
static struct macsec_rx_sc *find_rx_sc(struct macsec_secy *secy, sci_t sci)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
|
|
for_each_rxsc(secy, rx_sc) {
|
|
if (rx_sc->sci == sci)
|
|
return rx_sc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct macsec_rx_sc *find_rx_sc_rtnl(struct macsec_secy *secy, sci_t sci)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
|
|
for_each_rxsc_rtnl(secy, rx_sc) {
|
|
if (rx_sc->sci == sci)
|
|
return rx_sc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static enum rx_handler_result handle_not_macsec(struct sk_buff *skb)
|
|
{
|
|
/* Deliver to the uncontrolled port by default */
|
|
enum rx_handler_result ret = RX_HANDLER_PASS;
|
|
struct ethhdr *hdr = eth_hdr(skb);
|
|
struct macsec_rxh_data *rxd;
|
|
struct macsec_dev *macsec;
|
|
|
|
rcu_read_lock();
|
|
rxd = macsec_data_rcu(skb->dev);
|
|
|
|
list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
|
|
struct sk_buff *nskb;
|
|
struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats);
|
|
struct net_device *ndev = macsec->secy.netdev;
|
|
|
|
/* When HW offload is enabled, HW decodes frames and strips the
|
|
* SecTAG, so we have to deduce which port to deliver to.
|
|
*/
|
|
if (macsec_get_ops(macsec, NULL) && netif_running(ndev)) {
|
|
if (hdr->h_proto == htons(ETH_P_PAE))
|
|
continue;
|
|
|
|
if (ndev->flags & IFF_PROMISC) {
|
|
nskb = skb_clone(skb, GFP_ATOMIC);
|
|
if (!nskb)
|
|
break;
|
|
|
|
count_rx(ndev, nskb->len);
|
|
nskb->dev = ndev;
|
|
netif_rx(nskb);
|
|
} else if (ether_addr_equal_64bits(hdr->h_dest,
|
|
ndev->dev_addr)) {
|
|
/* HW offload enabled, divert skb */
|
|
skb->dev = ndev;
|
|
skb->pkt_type = PACKET_HOST;
|
|
count_rx(ndev, skb->len);
|
|
ret = RX_HANDLER_ANOTHER;
|
|
goto out;
|
|
} else if (is_multicast_ether_addr_64bits(hdr->h_dest)) {
|
|
/* multicast frame, deliver on this port as well */
|
|
nskb = skb_clone(skb, GFP_ATOMIC);
|
|
if (!nskb)
|
|
break;
|
|
|
|
nskb->dev = ndev;
|
|
if (ether_addr_equal_64bits(hdr->h_dest, ndev->broadcast))
|
|
nskb->pkt_type = PACKET_BROADCAST;
|
|
else
|
|
nskb->pkt_type = PACKET_MULTICAST;
|
|
|
|
count_rx(ndev, nskb->len);
|
|
netif_rx(nskb);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* 10.6 If the management control validateFrames is not
|
|
* Strict, frames without a SecTAG are received, counted, and
|
|
* delivered to the Controlled Port
|
|
*/
|
|
if (macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) {
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.InPktsNoTag++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
continue;
|
|
}
|
|
|
|
/* deliver on this port */
|
|
nskb = skb_clone(skb, GFP_ATOMIC);
|
|
if (!nskb)
|
|
break;
|
|
|
|
nskb->dev = macsec->secy.netdev;
|
|
|
|
if (netif_rx(nskb) == NET_RX_SUCCESS) {
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.InPktsUntagged++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
}
|
|
}
|
|
|
|
out:
|
|
rcu_read_unlock();
|
|
return ret;
|
|
}
|
|
|
|
static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb)
|
|
{
|
|
struct sk_buff *skb = *pskb;
|
|
struct net_device *dev = skb->dev;
|
|
struct macsec_eth_header *hdr;
|
|
struct macsec_secy *secy = NULL;
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct macsec_rx_sa *rx_sa;
|
|
struct macsec_rxh_data *rxd;
|
|
struct macsec_dev *macsec;
|
|
sci_t sci;
|
|
u32 pn;
|
|
bool cbit;
|
|
struct pcpu_rx_sc_stats *rxsc_stats;
|
|
struct pcpu_secy_stats *secy_stats;
|
|
bool pulled_sci;
|
|
int ret;
|
|
|
|
if (skb_headroom(skb) < ETH_HLEN)
|
|
goto drop_direct;
|
|
|
|
hdr = macsec_ethhdr(skb);
|
|
if (hdr->eth.h_proto != htons(ETH_P_MACSEC))
|
|
return handle_not_macsec(skb);
|
|
|
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
|
*pskb = skb;
|
|
if (!skb)
|
|
return RX_HANDLER_CONSUMED;
|
|
|
|
pulled_sci = pskb_may_pull(skb, macsec_extra_len(true));
|
|
if (!pulled_sci) {
|
|
if (!pskb_may_pull(skb, macsec_extra_len(false)))
|
|
goto drop_direct;
|
|
}
|
|
|
|
hdr = macsec_ethhdr(skb);
|
|
|
|
/* Frames with a SecTAG that has the TCI E bit set but the C
|
|
* bit clear are discarded, as this reserved encoding is used
|
|
* to identify frames with a SecTAG that are not to be
|
|
* delivered to the Controlled Port.
|
|
*/
|
|
if ((hdr->tci_an & (MACSEC_TCI_C | MACSEC_TCI_E)) == MACSEC_TCI_E)
|
|
return RX_HANDLER_PASS;
|
|
|
|
/* now, pull the extra length */
|
|
if (hdr->tci_an & MACSEC_TCI_SC) {
|
|
if (!pulled_sci)
|
|
goto drop_direct;
|
|
}
|
|
|
|
/* ethernet header is part of crypto processing */
|
|
skb_push(skb, ETH_HLEN);
|
|
|
|
macsec_skb_cb(skb)->has_sci = !!(hdr->tci_an & MACSEC_TCI_SC);
|
|
macsec_skb_cb(skb)->assoc_num = hdr->tci_an & MACSEC_AN_MASK;
|
|
sci = macsec_frame_sci(hdr, macsec_skb_cb(skb)->has_sci);
|
|
|
|
rcu_read_lock();
|
|
rxd = macsec_data_rcu(skb->dev);
|
|
|
|
list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
|
|
struct macsec_rx_sc *sc = find_rx_sc(&macsec->secy, sci);
|
|
sc = sc ? macsec_rxsc_get(sc) : NULL;
|
|
|
|
if (sc) {
|
|
secy = &macsec->secy;
|
|
rx_sc = sc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!secy)
|
|
goto nosci;
|
|
|
|
dev = secy->netdev;
|
|
macsec = macsec_priv(dev);
|
|
secy_stats = this_cpu_ptr(macsec->stats);
|
|
rxsc_stats = this_cpu_ptr(rx_sc->stats);
|
|
|
|
if (!macsec_validate_skb(skb, secy->icv_len)) {
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.InPktsBadTag++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
goto drop_nosa;
|
|
}
|
|
|
|
rx_sa = macsec_rxsa_get(rx_sc->sa[macsec_skb_cb(skb)->assoc_num]);
|
|
if (!rx_sa) {
|
|
/* 10.6.1 if the SA is not in use */
|
|
|
|
/* If validateFrames is Strict or the C bit in the
|
|
* SecTAG is set, discard
|
|
*/
|
|
if (hdr->tci_an & MACSEC_TCI_C ||
|
|
secy->validate_frames == MACSEC_VALIDATE_STRICT) {
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
rxsc_stats->stats.InPktsNotUsingSA++;
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
goto drop_nosa;
|
|
}
|
|
|
|
/* not Strict, the frame (with the SecTAG and ICV
|
|
* removed) is delivered to the Controlled Port.
|
|
*/
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
rxsc_stats->stats.InPktsUnusedSA++;
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
goto deliver;
|
|
}
|
|
|
|
/* First, PN check to avoid decrypting obviously wrong packets */
|
|
pn = ntohl(hdr->packet_number);
|
|
if (secy->replay_protect) {
|
|
bool late;
|
|
|
|
spin_lock(&rx_sa->lock);
|
|
late = rx_sa->next_pn >= secy->replay_window &&
|
|
pn < (rx_sa->next_pn - secy->replay_window);
|
|
spin_unlock(&rx_sa->lock);
|
|
|
|
if (late) {
|
|
u64_stats_update_begin(&rxsc_stats->syncp);
|
|
rxsc_stats->stats.InPktsLate++;
|
|
u64_stats_update_end(&rxsc_stats->syncp);
|
|
goto drop;
|
|
}
|
|
}
|
|
|
|
macsec_skb_cb(skb)->rx_sa = rx_sa;
|
|
|
|
/* Disabled && !changed text => skip validation */
|
|
if (hdr->tci_an & MACSEC_TCI_C ||
|
|
secy->validate_frames != MACSEC_VALIDATE_DISABLED)
|
|
skb = macsec_decrypt(skb, dev, rx_sa, sci, secy);
|
|
|
|
if (IS_ERR(skb)) {
|
|
/* the decrypt callback needs the reference */
|
|
if (PTR_ERR(skb) != -EINPROGRESS) {
|
|
macsec_rxsa_put(rx_sa);
|
|
macsec_rxsc_put(rx_sc);
|
|
}
|
|
rcu_read_unlock();
|
|
*pskb = NULL;
|
|
return RX_HANDLER_CONSUMED;
|
|
}
|
|
|
|
if (!macsec_post_decrypt(skb, secy, pn))
|
|
goto drop;
|
|
|
|
deliver:
|
|
macsec_finalize_skb(skb, secy->icv_len,
|
|
macsec_extra_len(macsec_skb_cb(skb)->has_sci));
|
|
macsec_reset_skb(skb, secy->netdev);
|
|
|
|
if (rx_sa)
|
|
macsec_rxsa_put(rx_sa);
|
|
macsec_rxsc_put(rx_sc);
|
|
|
|
skb_orphan(skb);
|
|
ret = gro_cells_receive(&macsec->gro_cells, skb);
|
|
if (ret == NET_RX_SUCCESS)
|
|
count_rx(dev, skb->len);
|
|
else
|
|
macsec->secy.netdev->stats.rx_dropped++;
|
|
|
|
rcu_read_unlock();
|
|
|
|
*pskb = NULL;
|
|
return RX_HANDLER_CONSUMED;
|
|
|
|
drop:
|
|
macsec_rxsa_put(rx_sa);
|
|
drop_nosa:
|
|
macsec_rxsc_put(rx_sc);
|
|
rcu_read_unlock();
|
|
drop_direct:
|
|
kfree_skb(skb);
|
|
*pskb = NULL;
|
|
return RX_HANDLER_CONSUMED;
|
|
|
|
nosci:
|
|
/* 10.6.1 if the SC is not found */
|
|
cbit = !!(hdr->tci_an & MACSEC_TCI_C);
|
|
if (!cbit)
|
|
macsec_finalize_skb(skb, DEFAULT_ICV_LEN,
|
|
macsec_extra_len(macsec_skb_cb(skb)->has_sci));
|
|
|
|
list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
|
|
struct sk_buff *nskb;
|
|
|
|
secy_stats = this_cpu_ptr(macsec->stats);
|
|
|
|
/* If validateFrames is Strict or the C bit in the
|
|
* SecTAG is set, discard
|
|
*/
|
|
if (cbit ||
|
|
macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) {
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.InPktsNoSCI++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
continue;
|
|
}
|
|
|
|
/* not strict, the frame (with the SecTAG and ICV
|
|
* removed) is delivered to the Controlled Port.
|
|
*/
|
|
nskb = skb_clone(skb, GFP_ATOMIC);
|
|
if (!nskb)
|
|
break;
|
|
|
|
macsec_reset_skb(nskb, macsec->secy.netdev);
|
|
|
|
ret = netif_rx(nskb);
|
|
if (ret == NET_RX_SUCCESS) {
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.InPktsUnknownSCI++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
} else {
|
|
macsec->secy.netdev->stats.rx_dropped++;
|
|
}
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
*pskb = skb;
|
|
return RX_HANDLER_PASS;
|
|
}
|
|
|
|
static struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len)
|
|
{
|
|
struct crypto_aead *tfm;
|
|
int ret;
|
|
|
|
/* Pick a sync gcm(aes) cipher to ensure order is preserved. */
|
|
tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
|
|
|
|
if (IS_ERR(tfm))
|
|
return tfm;
|
|
|
|
ret = crypto_aead_setkey(tfm, key, key_len);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
ret = crypto_aead_setauthsize(tfm, icv_len);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
return tfm;
|
|
fail:
|
|
crypto_free_aead(tfm);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len,
|
|
int icv_len)
|
|
{
|
|
rx_sa->stats = alloc_percpu(struct macsec_rx_sa_stats);
|
|
if (!rx_sa->stats)
|
|
return -ENOMEM;
|
|
|
|
rx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len);
|
|
if (IS_ERR(rx_sa->key.tfm)) {
|
|
free_percpu(rx_sa->stats);
|
|
return PTR_ERR(rx_sa->key.tfm);
|
|
}
|
|
|
|
rx_sa->active = false;
|
|
rx_sa->next_pn = 1;
|
|
atomic_set(&rx_sa->refcnt, 1);
|
|
spin_lock_init(&rx_sa->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void clear_rx_sa(struct macsec_rx_sa *rx_sa)
|
|
{
|
|
rx_sa->active = false;
|
|
|
|
macsec_rxsa_put(rx_sa);
|
|
}
|
|
|
|
static void free_rx_sc(struct macsec_rx_sc *rx_sc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MACSEC_NUM_AN; i++) {
|
|
struct macsec_rx_sa *sa = rtnl_dereference(rx_sc->sa[i]);
|
|
|
|
RCU_INIT_POINTER(rx_sc->sa[i], NULL);
|
|
if (sa)
|
|
clear_rx_sa(sa);
|
|
}
|
|
|
|
macsec_rxsc_put(rx_sc);
|
|
}
|
|
|
|
static struct macsec_rx_sc *del_rx_sc(struct macsec_secy *secy, sci_t sci)
|
|
{
|
|
struct macsec_rx_sc *rx_sc, __rcu **rx_scp;
|
|
|
|
for (rx_scp = &secy->rx_sc, rx_sc = rtnl_dereference(*rx_scp);
|
|
rx_sc;
|
|
rx_scp = &rx_sc->next, rx_sc = rtnl_dereference(*rx_scp)) {
|
|
if (rx_sc->sci == sci) {
|
|
if (rx_sc->active)
|
|
secy->n_rx_sc--;
|
|
rcu_assign_pointer(*rx_scp, rx_sc->next);
|
|
return rx_sc;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct macsec_rx_sc *create_rx_sc(struct net_device *dev, sci_t sci)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct macsec_dev *macsec;
|
|
struct net_device *real_dev = macsec_priv(dev)->real_dev;
|
|
struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
|
|
struct macsec_secy *secy;
|
|
|
|
list_for_each_entry(macsec, &rxd->secys, secys) {
|
|
if (find_rx_sc_rtnl(&macsec->secy, sci))
|
|
return ERR_PTR(-EEXIST);
|
|
}
|
|
|
|
rx_sc = kzalloc(sizeof(*rx_sc), GFP_KERNEL);
|
|
if (!rx_sc)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
rx_sc->stats = netdev_alloc_pcpu_stats(struct pcpu_rx_sc_stats);
|
|
if (!rx_sc->stats) {
|
|
kfree(rx_sc);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
rx_sc->sci = sci;
|
|
rx_sc->active = true;
|
|
atomic_set(&rx_sc->refcnt, 1);
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
rcu_assign_pointer(rx_sc->next, secy->rx_sc);
|
|
rcu_assign_pointer(secy->rx_sc, rx_sc);
|
|
|
|
if (rx_sc->active)
|
|
secy->n_rx_sc++;
|
|
|
|
return rx_sc;
|
|
}
|
|
|
|
static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len,
|
|
int icv_len)
|
|
{
|
|
tx_sa->stats = alloc_percpu(struct macsec_tx_sa_stats);
|
|
if (!tx_sa->stats)
|
|
return -ENOMEM;
|
|
|
|
tx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len);
|
|
if (IS_ERR(tx_sa->key.tfm)) {
|
|
free_percpu(tx_sa->stats);
|
|
return PTR_ERR(tx_sa->key.tfm);
|
|
}
|
|
|
|
tx_sa->active = false;
|
|
atomic_set(&tx_sa->refcnt, 1);
|
|
spin_lock_init(&tx_sa->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void clear_tx_sa(struct macsec_tx_sa *tx_sa)
|
|
{
|
|
tx_sa->active = false;
|
|
|
|
macsec_txsa_put(tx_sa);
|
|
}
|
|
|
|
static struct genl_family macsec_fam;
|
|
|
|
static struct net_device *get_dev_from_nl(struct net *net,
|
|
struct nlattr **attrs)
|
|
{
|
|
int ifindex = nla_get_u32(attrs[MACSEC_ATTR_IFINDEX]);
|
|
struct net_device *dev;
|
|
|
|
dev = __dev_get_by_index(net, ifindex);
|
|
if (!dev)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
if (!netif_is_macsec(dev))
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
return dev;
|
|
}
|
|
|
|
static sci_t nla_get_sci(const struct nlattr *nla)
|
|
{
|
|
return (__force sci_t)nla_get_u64(nla);
|
|
}
|
|
|
|
static int nla_put_sci(struct sk_buff *skb, int attrtype, sci_t value,
|
|
int padattr)
|
|
{
|
|
return nla_put_u64_64bit(skb, attrtype, (__force u64)value, padattr);
|
|
}
|
|
|
|
static struct macsec_tx_sa *get_txsa_from_nl(struct net *net,
|
|
struct nlattr **attrs,
|
|
struct nlattr **tb_sa,
|
|
struct net_device **devp,
|
|
struct macsec_secy **secyp,
|
|
struct macsec_tx_sc **scp,
|
|
u8 *assoc_num)
|
|
{
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_tx_sc *tx_sc;
|
|
struct macsec_tx_sa *tx_sa;
|
|
|
|
if (!tb_sa[MACSEC_SA_ATTR_AN])
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
*assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
|
|
|
|
dev = get_dev_from_nl(net, attrs);
|
|
if (IS_ERR(dev))
|
|
return ERR_CAST(dev);
|
|
|
|
if (*assoc_num >= MACSEC_NUM_AN)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
tx_sc = &secy->tx_sc;
|
|
|
|
tx_sa = rtnl_dereference(tx_sc->sa[*assoc_num]);
|
|
if (!tx_sa)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
*devp = dev;
|
|
*scp = tx_sc;
|
|
*secyp = secy;
|
|
return tx_sa;
|
|
}
|
|
|
|
static struct macsec_rx_sc *get_rxsc_from_nl(struct net *net,
|
|
struct nlattr **attrs,
|
|
struct nlattr **tb_rxsc,
|
|
struct net_device **devp,
|
|
struct macsec_secy **secyp)
|
|
{
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc;
|
|
sci_t sci;
|
|
|
|
dev = get_dev_from_nl(net, attrs);
|
|
if (IS_ERR(dev))
|
|
return ERR_CAST(dev);
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
|
|
if (!tb_rxsc[MACSEC_RXSC_ATTR_SCI])
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
|
|
rx_sc = find_rx_sc_rtnl(secy, sci);
|
|
if (!rx_sc)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
*secyp = secy;
|
|
*devp = dev;
|
|
|
|
return rx_sc;
|
|
}
|
|
|
|
static struct macsec_rx_sa *get_rxsa_from_nl(struct net *net,
|
|
struct nlattr **attrs,
|
|
struct nlattr **tb_rxsc,
|
|
struct nlattr **tb_sa,
|
|
struct net_device **devp,
|
|
struct macsec_secy **secyp,
|
|
struct macsec_rx_sc **scp,
|
|
u8 *assoc_num)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct macsec_rx_sa *rx_sa;
|
|
|
|
if (!tb_sa[MACSEC_SA_ATTR_AN])
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
*assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
|
|
if (*assoc_num >= MACSEC_NUM_AN)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
rx_sc = get_rxsc_from_nl(net, attrs, tb_rxsc, devp, secyp);
|
|
if (IS_ERR(rx_sc))
|
|
return ERR_CAST(rx_sc);
|
|
|
|
rx_sa = rtnl_dereference(rx_sc->sa[*assoc_num]);
|
|
if (!rx_sa)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
*scp = rx_sc;
|
|
return rx_sa;
|
|
}
|
|
|
|
|
|
static const struct nla_policy macsec_genl_policy[NUM_MACSEC_ATTR] = {
|
|
[MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
|
|
[MACSEC_ATTR_RXSC_CONFIG] = { .type = NLA_NESTED },
|
|
[MACSEC_ATTR_SA_CONFIG] = { .type = NLA_NESTED },
|
|
};
|
|
|
|
static const struct nla_policy macsec_genl_rxsc_policy[NUM_MACSEC_RXSC_ATTR] = {
|
|
[MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
|
|
[MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
|
|
};
|
|
|
|
static const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = {
|
|
[MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
|
|
[MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
|
|
[MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
|
|
[MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY,
|
|
.len = MACSEC_KEYID_LEN, },
|
|
[MACSEC_SA_ATTR_KEY] = { .type = NLA_BINARY,
|
|
.len = MACSEC_MAX_KEY_LEN, },
|
|
};
|
|
|
|
/* Offloads an operation to a device driver */
|
|
static int macsec_offload(int (* const func)(struct macsec_context *),
|
|
struct macsec_context *ctx)
|
|
{
|
|
int ret;
|
|
|
|
if (unlikely(!func))
|
|
return 0;
|
|
|
|
if (ctx->is_phy)
|
|
mutex_lock(&ctx->phydev->lock);
|
|
|
|
/* Phase I: prepare. The drive should fail here if there are going to be
|
|
* issues in the commit phase.
|
|
*/
|
|
ctx->prepare = true;
|
|
ret = (*func)(ctx);
|
|
if (ret)
|
|
goto phy_unlock;
|
|
|
|
/* Phase II: commit. This step cannot fail. */
|
|
ctx->prepare = false;
|
|
ret = (*func)(ctx);
|
|
/* This should never happen: commit is not allowed to fail */
|
|
if (unlikely(ret))
|
|
WARN(1, "MACsec offloading commit failed (%d)\n", ret);
|
|
|
|
phy_unlock:
|
|
if (ctx->is_phy)
|
|
mutex_unlock(&ctx->phydev->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa)
|
|
{
|
|
if (!attrs[MACSEC_ATTR_SA_CONFIG])
|
|
return -EINVAL;
|
|
|
|
if (nla_parse_nested(tb_sa, MACSEC_SA_ATTR_MAX,
|
|
attrs[MACSEC_ATTR_SA_CONFIG],
|
|
macsec_genl_sa_policy, NULL))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_rxsc_config(struct nlattr **attrs, struct nlattr **tb_rxsc)
|
|
{
|
|
if (!attrs[MACSEC_ATTR_RXSC_CONFIG])
|
|
return -EINVAL;
|
|
|
|
if (nla_parse_nested(tb_rxsc, MACSEC_RXSC_ATTR_MAX,
|
|
attrs[MACSEC_ATTR_RXSC_CONFIG],
|
|
macsec_genl_rxsc_policy, NULL))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool validate_add_rxsa(struct nlattr **attrs)
|
|
{
|
|
if (!attrs[MACSEC_SA_ATTR_AN] ||
|
|
!attrs[MACSEC_SA_ATTR_KEY] ||
|
|
!attrs[MACSEC_SA_ATTR_KEYID])
|
|
return false;
|
|
|
|
if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
|
|
return false;
|
|
|
|
if (attrs[MACSEC_SA_ATTR_PN] && nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0)
|
|
return false;
|
|
|
|
if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
|
|
if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
|
|
return false;
|
|
}
|
|
|
|
if (nla_len(attrs[MACSEC_SA_ATTR_KEYID]) != MACSEC_KEYID_LEN)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct net_device *dev;
|
|
struct nlattr **attrs = info->attrs;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc, *prev_sc;
|
|
struct macsec_rx_sa *rx_sa;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
unsigned char assoc_num;
|
|
struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
|
|
struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
|
|
bool was_active;
|
|
int err;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_sa_config(attrs, tb_sa))
|
|
return -EINVAL;
|
|
|
|
if (parse_rxsc_config(attrs, tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
if (!validate_add_rxsa(tb_sa))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy);
|
|
if (IS_ERR(rx_sc)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(rx_sc);
|
|
}
|
|
|
|
assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
|
|
|
|
if (nla_len(tb_sa[MACSEC_SA_ATTR_KEY]) != secy->key_len) {
|
|
pr_notice("macsec: nl: add_rxsa: bad key length: %d != %d\n",
|
|
nla_len(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len);
|
|
rtnl_unlock();
|
|
return -EINVAL;
|
|
}
|
|
|
|
rx_sa = rtnl_dereference(rx_sc->sa[assoc_num]);
|
|
if (rx_sa) {
|
|
rtnl_unlock();
|
|
return -EBUSY;
|
|
}
|
|
|
|
rx_sa = kmalloc(sizeof(*rx_sa), GFP_KERNEL);
|
|
if (!rx_sa) {
|
|
rtnl_unlock();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
err = init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
|
|
secy->key_len, secy->icv_len);
|
|
if (err < 0) {
|
|
kfree(rx_sa);
|
|
rtnl_unlock();
|
|
return err;
|
|
}
|
|
|
|
if (tb_sa[MACSEC_SA_ATTR_PN]) {
|
|
spin_lock_bh(&rx_sa->lock);
|
|
rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
|
|
spin_unlock_bh(&rx_sa->lock);
|
|
}
|
|
|
|
was_active = rx_sa->active;
|
|
if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
|
|
rx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
|
|
|
|
prev_sc = rx_sa->sc;
|
|
rx_sa->sc = rx_sc;
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = assoc_num;
|
|
ctx.sa.rx_sa = rx_sa;
|
|
ctx.secy = secy;
|
|
memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
|
|
MACSEC_KEYID_LEN);
|
|
|
|
err = macsec_offload(ops->mdo_add_rxsa, &ctx);
|
|
if (err) {
|
|
rx_sa->active = was_active;
|
|
rx_sa->sc = prev_sc;
|
|
kfree(rx_sa);
|
|
rtnl_unlock();
|
|
return err;
|
|
}
|
|
}
|
|
|
|
nla_memcpy(rx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN);
|
|
rcu_assign_pointer(rx_sc->sa[assoc_num], rx_sa);
|
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool validate_add_rxsc(struct nlattr **attrs)
|
|
{
|
|
if (!attrs[MACSEC_RXSC_ATTR_SCI])
|
|
return false;
|
|
|
|
if (attrs[MACSEC_RXSC_ATTR_ACTIVE]) {
|
|
if (nla_get_u8(attrs[MACSEC_RXSC_ATTR_ACTIVE]) > 1)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct net_device *dev;
|
|
sci_t sci = MACSEC_UNDEF_SCI;
|
|
struct nlattr **attrs = info->attrs;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
bool was_active;
|
|
int ret;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_rxsc_config(attrs, tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
if (!validate_add_rxsc(tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
dev = get_dev_from_nl(genl_info_net(info), attrs);
|
|
if (IS_ERR(dev)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(dev);
|
|
}
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
|
|
|
|
rx_sc = create_rx_sc(dev, sci);
|
|
if (IS_ERR(rx_sc)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(rx_sc);
|
|
}
|
|
|
|
was_active = rx_sc->active;
|
|
if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE])
|
|
rx_sc->active = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]);
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.rx_sc = rx_sc;
|
|
ctx.secy = secy;
|
|
|
|
ret = macsec_offload(ops->mdo_add_rxsc, &ctx);
|
|
if (ret) {
|
|
rx_sc->active = was_active;
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool validate_add_txsa(struct nlattr **attrs)
|
|
{
|
|
if (!attrs[MACSEC_SA_ATTR_AN] ||
|
|
!attrs[MACSEC_SA_ATTR_PN] ||
|
|
!attrs[MACSEC_SA_ATTR_KEY] ||
|
|
!attrs[MACSEC_SA_ATTR_KEYID])
|
|
return false;
|
|
|
|
if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
|
|
return false;
|
|
|
|
if (nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0)
|
|
return false;
|
|
|
|
if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
|
|
if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
|
|
return false;
|
|
}
|
|
|
|
if (nla_len(attrs[MACSEC_SA_ATTR_KEYID]) != MACSEC_KEYID_LEN)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct net_device *dev;
|
|
struct nlattr **attrs = info->attrs;
|
|
struct macsec_secy *secy;
|
|
struct macsec_tx_sc *tx_sc;
|
|
struct macsec_tx_sa *tx_sa;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
unsigned char assoc_num;
|
|
struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
|
|
bool was_operational, was_active;
|
|
u32 prev_pn;
|
|
int err;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_sa_config(attrs, tb_sa))
|
|
return -EINVAL;
|
|
|
|
if (!validate_add_txsa(tb_sa))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
dev = get_dev_from_nl(genl_info_net(info), attrs);
|
|
if (IS_ERR(dev)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(dev);
|
|
}
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
tx_sc = &secy->tx_sc;
|
|
|
|
assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
|
|
|
|
if (nla_len(tb_sa[MACSEC_SA_ATTR_KEY]) != secy->key_len) {
|
|
pr_notice("macsec: nl: add_txsa: bad key length: %d != %d\n",
|
|
nla_len(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len);
|
|
rtnl_unlock();
|
|
return -EINVAL;
|
|
}
|
|
|
|
tx_sa = rtnl_dereference(tx_sc->sa[assoc_num]);
|
|
if (tx_sa) {
|
|
rtnl_unlock();
|
|
return -EBUSY;
|
|
}
|
|
|
|
tx_sa = kmalloc(sizeof(*tx_sa), GFP_KERNEL);
|
|
if (!tx_sa) {
|
|
rtnl_unlock();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
err = init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
|
|
secy->key_len, secy->icv_len);
|
|
if (err < 0) {
|
|
kfree(tx_sa);
|
|
rtnl_unlock();
|
|
return err;
|
|
}
|
|
|
|
spin_lock_bh(&tx_sa->lock);
|
|
prev_pn = tx_sa->next_pn;
|
|
tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
|
|
was_active = tx_sa->active;
|
|
if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
|
|
tx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
|
|
|
|
was_operational = secy->operational;
|
|
if (assoc_num == tx_sc->encoding_sa && tx_sa->active)
|
|
secy->operational = true;
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = assoc_num;
|
|
ctx.sa.tx_sa = tx_sa;
|
|
ctx.secy = secy;
|
|
memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
|
|
MACSEC_KEYID_LEN);
|
|
|
|
err = macsec_offload(ops->mdo_add_txsa, &ctx);
|
|
if (err) {
|
|
spin_lock_bh(&tx_sa->lock);
|
|
tx_sa->next_pn = prev_pn;
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
|
|
tx_sa->active = was_active;
|
|
secy->operational = was_operational;
|
|
kfree(tx_sa);
|
|
rtnl_unlock();
|
|
return err;
|
|
}
|
|
}
|
|
|
|
nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN);
|
|
rcu_assign_pointer(tx_sc->sa[assoc_num], tx_sa);
|
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct macsec_rx_sa *rx_sa;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
u8 assoc_num;
|
|
struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
|
|
struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
|
|
int ret;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_sa_config(attrs, tb_sa))
|
|
return -EINVAL;
|
|
|
|
if (parse_rxsc_config(attrs, tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
rx_sa = get_rxsa_from_nl(genl_info_net(info), attrs, tb_rxsc, tb_sa,
|
|
&dev, &secy, &rx_sc, &assoc_num);
|
|
if (IS_ERR(rx_sa)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(rx_sa);
|
|
}
|
|
|
|
if (rx_sa->active) {
|
|
rtnl_unlock();
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = assoc_num;
|
|
ctx.sa.rx_sa = rx_sa;
|
|
ctx.secy = secy;
|
|
|
|
ret = macsec_offload(ops->mdo_del_rxsa, &ctx);
|
|
if (ret) {
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
RCU_INIT_POINTER(rx_sc->sa[assoc_num], NULL);
|
|
clear_rx_sa(rx_sa);
|
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
sci_t sci;
|
|
struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
|
|
int ret;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_rxsc_config(attrs, tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
if (!tb_rxsc[MACSEC_RXSC_ATTR_SCI])
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
dev = get_dev_from_nl(genl_info_net(info), info->attrs);
|
|
if (IS_ERR(dev)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(dev);
|
|
}
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
|
|
|
|
rx_sc = del_rx_sc(secy, sci);
|
|
if (!rx_sc) {
|
|
rtnl_unlock();
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.rx_sc = rx_sc;
|
|
ctx.secy = secy;
|
|
ret = macsec_offload(ops->mdo_del_rxsc, &ctx);
|
|
if (ret) {
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
free_rx_sc(rx_sc);
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_tx_sc *tx_sc;
|
|
struct macsec_tx_sa *tx_sa;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
u8 assoc_num;
|
|
struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
|
|
int ret;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_sa_config(attrs, tb_sa))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
tx_sa = get_txsa_from_nl(genl_info_net(info), attrs, tb_sa,
|
|
&dev, &secy, &tx_sc, &assoc_num);
|
|
if (IS_ERR(tx_sa)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(tx_sa);
|
|
}
|
|
|
|
if (tx_sa->active) {
|
|
rtnl_unlock();
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = assoc_num;
|
|
ctx.sa.tx_sa = tx_sa;
|
|
ctx.secy = secy;
|
|
|
|
ret = macsec_offload(ops->mdo_del_txsa, &ctx);
|
|
if (ret) {
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
RCU_INIT_POINTER(tx_sc->sa[assoc_num], NULL);
|
|
clear_tx_sa(tx_sa);
|
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool validate_upd_sa(struct nlattr **attrs)
|
|
{
|
|
if (!attrs[MACSEC_SA_ATTR_AN] ||
|
|
attrs[MACSEC_SA_ATTR_KEY] ||
|
|
attrs[MACSEC_SA_ATTR_KEYID])
|
|
return false;
|
|
|
|
if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
|
|
return false;
|
|
|
|
if (attrs[MACSEC_SA_ATTR_PN] && nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0)
|
|
return false;
|
|
|
|
if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
|
|
if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_tx_sc *tx_sc;
|
|
struct macsec_tx_sa *tx_sa;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
u8 assoc_num;
|
|
struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
|
|
bool was_operational, was_active;
|
|
u32 prev_pn = 0;
|
|
int ret = 0;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_sa_config(attrs, tb_sa))
|
|
return -EINVAL;
|
|
|
|
if (!validate_upd_sa(tb_sa))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
tx_sa = get_txsa_from_nl(genl_info_net(info), attrs, tb_sa,
|
|
&dev, &secy, &tx_sc, &assoc_num);
|
|
if (IS_ERR(tx_sa)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(tx_sa);
|
|
}
|
|
|
|
if (tb_sa[MACSEC_SA_ATTR_PN]) {
|
|
spin_lock_bh(&tx_sa->lock);
|
|
prev_pn = tx_sa->next_pn;
|
|
tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
}
|
|
|
|
was_active = tx_sa->active;
|
|
if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
|
|
tx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
|
|
|
|
was_operational = secy->operational;
|
|
if (assoc_num == tx_sc->encoding_sa)
|
|
secy->operational = tx_sa->active;
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = assoc_num;
|
|
ctx.sa.tx_sa = tx_sa;
|
|
ctx.secy = secy;
|
|
|
|
ret = macsec_offload(ops->mdo_upd_txsa, &ctx);
|
|
if (ret) {
|
|
if (tb_sa[MACSEC_SA_ATTR_PN]) {
|
|
spin_lock_bh(&tx_sa->lock);
|
|
tx_sa->next_pn = prev_pn;
|
|
spin_unlock_bh(&tx_sa->lock);
|
|
}
|
|
|
|
tx_sa->active = was_active;
|
|
secy->operational = was_operational;
|
|
}
|
|
}
|
|
|
|
rtnl_unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct macsec_rx_sa *rx_sa;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
u8 assoc_num;
|
|
struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
|
|
struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
|
|
bool was_active;
|
|
u32 prev_pn = 0;
|
|
int ret = 0;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_rxsc_config(attrs, tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
if (parse_sa_config(attrs, tb_sa))
|
|
return -EINVAL;
|
|
|
|
if (!validate_upd_sa(tb_sa))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
rx_sa = get_rxsa_from_nl(genl_info_net(info), attrs, tb_rxsc, tb_sa,
|
|
&dev, &secy, &rx_sc, &assoc_num);
|
|
if (IS_ERR(rx_sa)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(rx_sa);
|
|
}
|
|
|
|
if (tb_sa[MACSEC_SA_ATTR_PN]) {
|
|
spin_lock_bh(&rx_sa->lock);
|
|
prev_pn = rx_sa->next_pn;
|
|
rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
|
|
spin_unlock_bh(&rx_sa->lock);
|
|
}
|
|
|
|
was_active = rx_sa->active;
|
|
if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
|
|
rx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = assoc_num;
|
|
ctx.sa.rx_sa = rx_sa;
|
|
ctx.secy = secy;
|
|
|
|
ret = macsec_offload(ops->mdo_upd_rxsa, &ctx);
|
|
if (ret) {
|
|
if (tb_sa[MACSEC_SA_ATTR_PN]) {
|
|
spin_lock_bh(&rx_sa->lock);
|
|
rx_sa->next_pn = prev_pn;
|
|
spin_unlock_bh(&rx_sa->lock);
|
|
}
|
|
|
|
rx_sa->active = was_active;
|
|
}
|
|
}
|
|
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
|
|
static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct nlattr **attrs = info->attrs;
|
|
struct net_device *dev;
|
|
struct macsec_secy *secy;
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
unsigned int prev_n_rx_sc;
|
|
bool was_active;
|
|
int ret;
|
|
|
|
if (!attrs[MACSEC_ATTR_IFINDEX])
|
|
return -EINVAL;
|
|
|
|
if (parse_rxsc_config(attrs, tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
if (!validate_add_rxsc(tb_rxsc))
|
|
return -EINVAL;
|
|
|
|
rtnl_lock();
|
|
rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy);
|
|
if (IS_ERR(rx_sc)) {
|
|
rtnl_unlock();
|
|
return PTR_ERR(rx_sc);
|
|
}
|
|
|
|
was_active = rx_sc->active;
|
|
prev_n_rx_sc = secy->n_rx_sc;
|
|
if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]) {
|
|
bool new = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]);
|
|
|
|
if (rx_sc->active != new)
|
|
secy->n_rx_sc += new ? 1 : -1;
|
|
|
|
rx_sc->active = new;
|
|
}
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.rx_sc = rx_sc;
|
|
ctx.secy = secy;
|
|
|
|
ret = macsec_offload(ops->mdo_upd_rxsc, &ctx);
|
|
if (ret) {
|
|
secy->n_rx_sc = prev_n_rx_sc;
|
|
rx_sc->active = was_active;
|
|
rtnl_unlock();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
rtnl_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_tx_sa_stats(struct net_device *dev, int an,
|
|
struct macsec_tx_sa *tx_sa,
|
|
struct macsec_tx_sa_stats *sum)
|
|
{
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
int err = -EOPNOTSUPP;
|
|
int cpu;
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = an;
|
|
ctx.sa.tx_sa = tx_sa;
|
|
ctx.stats.tx_sa_stats = sum;
|
|
ctx.secy = &macsec_priv(dev)->secy;
|
|
err = macsec_offload(ops->mdo_get_tx_sa_stats, &ctx);
|
|
}
|
|
|
|
if (err == -EOPNOTSUPP) {
|
|
for_each_possible_cpu(cpu) {
|
|
const struct macsec_tx_sa_stats *stats =
|
|
per_cpu_ptr(tx_sa->stats, cpu);
|
|
|
|
sum->OutPktsProtected += stats->OutPktsProtected;
|
|
sum->OutPktsEncrypted += stats->OutPktsEncrypted;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int copy_tx_sa_stats(struct sk_buff *skb, struct macsec_tx_sa_stats *sum)
|
|
{
|
|
if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, sum->OutPktsProtected) ||
|
|
nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, sum->OutPktsEncrypted))
|
|
return -EMSGSIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_rx_sa_stats(struct net_device *dev,
|
|
struct macsec_rx_sc *rx_sc, int an,
|
|
struct macsec_rx_sa *rx_sa,
|
|
struct macsec_rx_sa_stats *sum)
|
|
{
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
int err = -EOPNOTSUPP;
|
|
int cpu;
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.sa.assoc_num = an;
|
|
ctx.sa.rx_sa = rx_sa;
|
|
ctx.stats.rx_sa_stats = sum;
|
|
ctx.secy = &macsec_priv(dev)->secy;
|
|
ctx.rx_sc = rx_sc;
|
|
err = macsec_offload(ops->mdo_get_rx_sa_stats, &ctx);
|
|
}
|
|
|
|
if (err == -EOPNOTSUPP) {
|
|
for_each_possible_cpu(cpu) {
|
|
const struct macsec_rx_sa_stats *stats =
|
|
per_cpu_ptr(rx_sa->stats, cpu);
|
|
|
|
sum->InPktsOK += stats->InPktsOK;
|
|
sum->InPktsInvalid += stats->InPktsInvalid;
|
|
sum->InPktsNotValid += stats->InPktsNotValid;
|
|
sum->InPktsNotUsingSA += stats->InPktsNotUsingSA;
|
|
sum->InPktsUnusedSA += stats->InPktsUnusedSA;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int copy_rx_sa_stats(struct sk_buff *skb,
|
|
struct macsec_rx_sa_stats *sum)
|
|
{
|
|
if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_OK, sum->InPktsOK) ||
|
|
nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, sum->InPktsInvalid) ||
|
|
nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, sum->InPktsNotValid) ||
|
|
nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, sum->InPktsNotUsingSA) ||
|
|
nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, sum->InPktsUnusedSA))
|
|
return -EMSGSIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_rx_sc_stats(struct net_device *dev,
|
|
struct macsec_rx_sc *rx_sc,
|
|
struct macsec_rx_sc_stats *sum)
|
|
{
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
int err = -EOPNOTSUPP;
|
|
int cpu;
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.stats.rx_sc_stats = sum;
|
|
ctx.secy = &macsec_priv(dev)->secy;
|
|
ctx.rx_sc = rx_sc;
|
|
err = macsec_offload(ops->mdo_get_rx_sc_stats, &ctx);
|
|
}
|
|
|
|
if (err == -EOPNOTSUPP) {
|
|
for_each_possible_cpu(cpu) {
|
|
const struct pcpu_rx_sc_stats *stats;
|
|
struct macsec_rx_sc_stats tmp;
|
|
unsigned int start;
|
|
|
|
stats = per_cpu_ptr(rx_sc->stats, cpu);
|
|
do {
|
|
start = u64_stats_fetch_begin_irq(&stats->syncp);
|
|
memcpy(&tmp, &stats->stats, sizeof(tmp));
|
|
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
|
|
|
|
sum->InOctetsValidated += tmp.InOctetsValidated;
|
|
sum->InOctetsDecrypted += tmp.InOctetsDecrypted;
|
|
sum->InPktsUnchecked += tmp.InPktsUnchecked;
|
|
sum->InPktsDelayed += tmp.InPktsDelayed;
|
|
sum->InPktsOK += tmp.InPktsOK;
|
|
sum->InPktsInvalid += tmp.InPktsInvalid;
|
|
sum->InPktsLate += tmp.InPktsLate;
|
|
sum->InPktsNotValid += tmp.InPktsNotValid;
|
|
sum->InPktsNotUsingSA += tmp.InPktsNotUsingSA;
|
|
sum->InPktsUnusedSA += tmp.InPktsUnusedSA;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int copy_rx_sc_stats(struct sk_buff *skb, struct macsec_rx_sc_stats *sum)
|
|
{
|
|
if (nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED,
|
|
sum->InOctetsValidated,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED,
|
|
sum->InOctetsDecrypted,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED,
|
|
sum->InPktsUnchecked,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED,
|
|
sum->InPktsDelayed,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK,
|
|
sum->InPktsOK,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID,
|
|
sum->InPktsInvalid,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE,
|
|
sum->InPktsLate,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID,
|
|
sum->InPktsNotValid,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA,
|
|
sum->InPktsNotUsingSA,
|
|
MACSEC_RXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA,
|
|
sum->InPktsUnusedSA,
|
|
MACSEC_RXSC_STATS_ATTR_PAD))
|
|
return -EMSGSIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_tx_sc_stats(struct net_device *dev, struct macsec_tx_sc_stats *sum)
|
|
{
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
int err = -EOPNOTSUPP;
|
|
int cpu;
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.stats.tx_sc_stats = sum;
|
|
ctx.secy = &macsec_priv(dev)->secy;
|
|
err = macsec_offload(ops->mdo_get_tx_sc_stats, &ctx);
|
|
}
|
|
|
|
if (err == -EOPNOTSUPP) {
|
|
for_each_possible_cpu(cpu) {
|
|
const struct pcpu_tx_sc_stats *stats;
|
|
struct macsec_tx_sc_stats tmp;
|
|
unsigned int start;
|
|
|
|
stats = per_cpu_ptr(macsec_priv(dev)->secy.tx_sc.stats, cpu);
|
|
do {
|
|
start = u64_stats_fetch_begin_irq(&stats->syncp);
|
|
memcpy(&tmp, &stats->stats, sizeof(tmp));
|
|
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
|
|
|
|
sum->OutPktsProtected += tmp.OutPktsProtected;
|
|
sum->OutPktsEncrypted += tmp.OutPktsEncrypted;
|
|
sum->OutOctetsProtected += tmp.OutOctetsProtected;
|
|
sum->OutOctetsEncrypted += tmp.OutOctetsEncrypted;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int copy_tx_sc_stats(struct sk_buff *skb, struct macsec_tx_sc_stats *sum)
|
|
{
|
|
if (nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED,
|
|
sum->OutPktsProtected,
|
|
MACSEC_TXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED,
|
|
sum->OutPktsEncrypted,
|
|
MACSEC_TXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED,
|
|
sum->OutOctetsProtected,
|
|
MACSEC_TXSC_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED,
|
|
sum->OutOctetsEncrypted,
|
|
MACSEC_TXSC_STATS_ATTR_PAD))
|
|
return -EMSGSIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_secy_stats(struct net_device *dev, struct macsec_dev_stats *sum)
|
|
{
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
int err = -EOPNOTSUPP;
|
|
int cpu;
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.stats.dev_stats = sum;
|
|
ctx.secy = &macsec_priv(dev)->secy;
|
|
err = macsec_offload(ops->mdo_get_dev_stats, &ctx);
|
|
}
|
|
|
|
if (err == -EOPNOTSUPP) {
|
|
for_each_possible_cpu(cpu) {
|
|
const struct pcpu_secy_stats *stats;
|
|
struct macsec_dev_stats tmp;
|
|
unsigned int start;
|
|
|
|
stats = per_cpu_ptr(macsec_priv(dev)->stats, cpu);
|
|
do {
|
|
start = u64_stats_fetch_begin_irq(&stats->syncp);
|
|
memcpy(&tmp, &stats->stats, sizeof(tmp));
|
|
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
|
|
|
|
sum->OutPktsUntagged += tmp.OutPktsUntagged;
|
|
sum->InPktsUntagged += tmp.InPktsUntagged;
|
|
sum->OutPktsTooLong += tmp.OutPktsTooLong;
|
|
sum->InPktsNoTag += tmp.InPktsNoTag;
|
|
sum->InPktsBadTag += tmp.InPktsBadTag;
|
|
sum->InPktsUnknownSCI += tmp.InPktsUnknownSCI;
|
|
sum->InPktsNoSCI += tmp.InPktsNoSCI;
|
|
sum->InPktsOverrun += tmp.InPktsOverrun;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int copy_secy_stats(struct sk_buff *skb, struct macsec_dev_stats *sum)
|
|
{
|
|
if (nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED,
|
|
sum->OutPktsUntagged,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED,
|
|
sum->InPktsUntagged,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG,
|
|
sum->OutPktsTooLong,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG,
|
|
sum->InPktsNoTag,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG,
|
|
sum->InPktsBadTag,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI,
|
|
sum->InPktsUnknownSCI,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI,
|
|
sum->InPktsNoSCI,
|
|
MACSEC_SECY_STATS_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN,
|
|
sum->InPktsOverrun,
|
|
MACSEC_SECY_STATS_ATTR_PAD))
|
|
return -EMSGSIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nla_put_secy(struct macsec_secy *secy, struct sk_buff *skb)
|
|
{
|
|
struct macsec_tx_sc *tx_sc = &secy->tx_sc;
|
|
struct nlattr *secy_nest = nla_nest_start(skb, MACSEC_ATTR_SECY);
|
|
|
|
if (!secy_nest)
|
|
return 1;
|
|
|
|
if (nla_put_sci(skb, MACSEC_SECY_ATTR_SCI, secy->sci,
|
|
MACSEC_SECY_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, MACSEC_SECY_ATTR_CIPHER_SUITE,
|
|
MACSEC_DEFAULT_CIPHER_ID,
|
|
MACSEC_SECY_ATTR_PAD) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_ICV_LEN, secy->icv_len) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_OPER, secy->operational) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_PROTECT, secy->protect_frames) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_REPLAY, secy->replay_protect) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_VALIDATE, secy->validate_frames) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_ENCRYPT, tx_sc->encrypt) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_INC_SCI, tx_sc->send_sci) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_ES, tx_sc->end_station) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_SCB, tx_sc->scb) ||
|
|
nla_put_u8(skb, MACSEC_SECY_ATTR_ENCODING_SA, tx_sc->encoding_sa))
|
|
goto cancel;
|
|
|
|
if (secy->replay_protect) {
|
|
if (nla_put_u32(skb, MACSEC_SECY_ATTR_WINDOW, secy->replay_window))
|
|
goto cancel;
|
|
}
|
|
|
|
nla_nest_end(skb, secy_nest);
|
|
return 0;
|
|
|
|
cancel:
|
|
nla_nest_cancel(skb, secy_nest);
|
|
return 1;
|
|
}
|
|
|
|
static int dump_secy(struct macsec_secy *secy, struct net_device *dev,
|
|
struct sk_buff *skb, struct netlink_callback *cb)
|
|
{
|
|
struct macsec_rx_sc *rx_sc;
|
|
struct macsec_tx_sc *tx_sc = &secy->tx_sc;
|
|
struct nlattr *txsa_list, *rxsc_list;
|
|
struct macsec_dev_stats dev_stats = {0, };
|
|
struct macsec_tx_sc_stats tx_sc_stats = {0, };
|
|
struct macsec_tx_sa_stats tx_sa_stats = {0, };
|
|
struct macsec_rx_sc_stats rx_sc_stats = {0, };
|
|
struct macsec_rx_sa_stats rx_sa_stats = {0, };
|
|
int i, j;
|
|
void *hdr;
|
|
struct nlattr *attr;
|
|
|
|
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
|
&macsec_fam, NLM_F_MULTI, MACSEC_CMD_GET_TXSC);
|
|
if (!hdr)
|
|
return -EMSGSIZE;
|
|
|
|
genl_dump_check_consistent(cb, hdr, &macsec_fam);
|
|
|
|
if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex))
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_secy(secy, skb))
|
|
goto nla_put_failure;
|
|
|
|
attr = nla_nest_start(skb, MACSEC_ATTR_TXSC_STATS);
|
|
if (!attr)
|
|
goto nla_put_failure;
|
|
|
|
get_tx_sc_stats(dev, &tx_sc_stats);
|
|
if (copy_tx_sc_stats(skb, &tx_sc_stats)) {
|
|
nla_nest_cancel(skb, attr);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, attr);
|
|
|
|
attr = nla_nest_start(skb, MACSEC_ATTR_SECY_STATS);
|
|
if (!attr)
|
|
goto nla_put_failure;
|
|
get_secy_stats(dev, &dev_stats);
|
|
if (copy_secy_stats(skb, &dev_stats)) {
|
|
nla_nest_cancel(skb, attr);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, attr);
|
|
|
|
txsa_list = nla_nest_start(skb, MACSEC_ATTR_TXSA_LIST);
|
|
if (!txsa_list)
|
|
goto nla_put_failure;
|
|
for (i = 0, j = 1; i < MACSEC_NUM_AN; i++) {
|
|
struct macsec_tx_sa *tx_sa = rtnl_dereference(tx_sc->sa[i]);
|
|
struct nlattr *txsa_nest;
|
|
|
|
if (!tx_sa)
|
|
continue;
|
|
|
|
txsa_nest = nla_nest_start(skb, j++);
|
|
if (!txsa_nest) {
|
|
nla_nest_cancel(skb, txsa_list);
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
attr = nla_nest_start(skb, MACSEC_SA_ATTR_STATS);
|
|
if (!attr) {
|
|
nla_nest_cancel(skb, txsa_nest);
|
|
nla_nest_cancel(skb, txsa_list);
|
|
goto nla_put_failure;
|
|
}
|
|
memset(&tx_sa_stats, 0, sizeof (tx_sa_stats));
|
|
get_tx_sa_stats(dev, i, tx_sa, &tx_sa_stats);
|
|
if (copy_tx_sa_stats(skb, &tx_sa_stats)) {
|
|
nla_nest_cancel(skb, attr);
|
|
nla_nest_cancel(skb, txsa_nest);
|
|
nla_nest_cancel(skb, txsa_list);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, attr);
|
|
|
|
if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) ||
|
|
nla_put_u32(skb, MACSEC_SA_ATTR_PN, tx_sa->next_pn) ||
|
|
nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, tx_sa->key.id) ||
|
|
nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, tx_sa->active)) {
|
|
nla_nest_cancel(skb, txsa_nest);
|
|
nla_nest_cancel(skb, txsa_list);
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
nla_nest_end(skb, txsa_nest);
|
|
}
|
|
nla_nest_end(skb, txsa_list);
|
|
|
|
rxsc_list = nla_nest_start(skb, MACSEC_ATTR_RXSC_LIST);
|
|
if (!rxsc_list)
|
|
goto nla_put_failure;
|
|
|
|
j = 1;
|
|
for_each_rxsc_rtnl(secy, rx_sc) {
|
|
int k;
|
|
struct nlattr *rxsa_list;
|
|
struct nlattr *rxsc_nest = nla_nest_start(skb, j++);
|
|
|
|
if (!rxsc_nest) {
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
if (nla_put_u8(skb, MACSEC_RXSC_ATTR_ACTIVE, rx_sc->active) ||
|
|
nla_put_sci(skb, MACSEC_RXSC_ATTR_SCI, rx_sc->sci,
|
|
MACSEC_RXSC_ATTR_PAD)) {
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
attr = nla_nest_start(skb, MACSEC_RXSC_ATTR_STATS);
|
|
if (!attr) {
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
memset(&rx_sc_stats, 0, sizeof(rx_sc_stats));
|
|
get_rx_sc_stats(dev, rx_sc, &rx_sc_stats);
|
|
if (copy_rx_sc_stats(skb, &rx_sc_stats)) {
|
|
nla_nest_cancel(skb, attr);
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, attr);
|
|
|
|
rxsa_list = nla_nest_start(skb, MACSEC_RXSC_ATTR_SA_LIST);
|
|
if (!rxsa_list) {
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
for (i = 0, k = 1; i < MACSEC_NUM_AN; i++) {
|
|
struct macsec_rx_sa *rx_sa = rtnl_dereference(rx_sc->sa[i]);
|
|
struct nlattr *rxsa_nest;
|
|
|
|
if (!rx_sa)
|
|
continue;
|
|
|
|
rxsa_nest = nla_nest_start(skb, k++);
|
|
if (!rxsa_nest) {
|
|
nla_nest_cancel(skb, rxsa_list);
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
attr = nla_nest_start(skb, MACSEC_SA_ATTR_STATS);
|
|
if (!attr) {
|
|
nla_nest_cancel(skb, rxsa_list);
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
memset(&rx_sa_stats, 0, sizeof(rx_sa_stats));
|
|
get_rx_sa_stats(dev, rx_sc, i, rx_sa, &rx_sa_stats);
|
|
if (copy_rx_sa_stats(skb, &rx_sa_stats)) {
|
|
nla_nest_cancel(skb, attr);
|
|
nla_nest_cancel(skb, rxsa_list);
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, attr);
|
|
|
|
if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) ||
|
|
nla_put_u32(skb, MACSEC_SA_ATTR_PN, rx_sa->next_pn) ||
|
|
nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, rx_sa->key.id) ||
|
|
nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, rx_sa->active)) {
|
|
nla_nest_cancel(skb, rxsa_nest);
|
|
nla_nest_cancel(skb, rxsc_nest);
|
|
nla_nest_cancel(skb, rxsc_list);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, rxsa_nest);
|
|
}
|
|
|
|
nla_nest_end(skb, rxsa_list);
|
|
nla_nest_end(skb, rxsc_nest);
|
|
}
|
|
|
|
nla_nest_end(skb, rxsc_list);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
genlmsg_cancel(skb, hdr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int macsec_generation = 1; /* protected by RTNL */
|
|
|
|
static int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb)
|
|
{
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net_device *dev;
|
|
int dev_idx, d;
|
|
|
|
dev_idx = cb->args[0];
|
|
|
|
d = 0;
|
|
rtnl_lock();
|
|
|
|
cb->seq = macsec_generation;
|
|
|
|
for_each_netdev(net, dev) {
|
|
struct macsec_secy *secy;
|
|
|
|
if (d < dev_idx)
|
|
goto next;
|
|
|
|
if (!netif_is_macsec(dev))
|
|
goto next;
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
if (dump_secy(secy, dev, skb, cb) < 0)
|
|
goto done;
|
|
next:
|
|
d++;
|
|
}
|
|
|
|
done:
|
|
rtnl_unlock();
|
|
cb->args[0] = d;
|
|
return skb->len;
|
|
}
|
|
|
|
static const struct genl_ops macsec_genl_ops[] = {
|
|
{
|
|
.cmd = MACSEC_CMD_GET_TXSC,
|
|
.dumpit = macsec_dump_txsc,
|
|
.policy = macsec_genl_policy,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_ADD_RXSC,
|
|
.doit = macsec_add_rxsc,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_DEL_RXSC,
|
|
.doit = macsec_del_rxsc,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_UPD_RXSC,
|
|
.doit = macsec_upd_rxsc,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_ADD_TXSA,
|
|
.doit = macsec_add_txsa,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_DEL_TXSA,
|
|
.doit = macsec_del_txsa,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_UPD_TXSA,
|
|
.doit = macsec_upd_txsa,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_ADD_RXSA,
|
|
.doit = macsec_add_rxsa,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_DEL_RXSA,
|
|
.doit = macsec_del_rxsa,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = MACSEC_CMD_UPD_RXSA,
|
|
.doit = macsec_upd_rxsa,
|
|
.policy = macsec_genl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
};
|
|
|
|
static struct genl_family macsec_fam __ro_after_init = {
|
|
.name = MACSEC_GENL_NAME,
|
|
.hdrsize = 0,
|
|
.version = MACSEC_GENL_VERSION,
|
|
.maxattr = MACSEC_ATTR_MAX,
|
|
.netnsok = true,
|
|
.module = THIS_MODULE,
|
|
.ops = macsec_genl_ops,
|
|
.n_ops = ARRAY_SIZE(macsec_genl_ops),
|
|
};
|
|
|
|
static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = netdev_priv(dev);
|
|
struct macsec_secy *secy = &macsec->secy;
|
|
struct macsec_tx_sc *tx_sc = &secy->tx_sc;
|
|
struct pcpu_secy_stats *secy_stats;
|
|
struct macsec_tx_sa *tx_sa;
|
|
int ret, len;
|
|
|
|
tx_sa = macsec_txsa_get(tx_sc->sa[tx_sc->encoding_sa]);
|
|
|
|
/* 10.5 */
|
|
if (!secy->protect_frames || macsec_get_ops(netdev_priv(dev), NULL)) {
|
|
secy_stats = this_cpu_ptr(macsec->stats);
|
|
u64_stats_update_begin(&secy_stats->syncp);
|
|
secy_stats->stats.OutPktsUntagged++;
|
|
u64_stats_update_end(&secy_stats->syncp);
|
|
skb->dev = macsec->real_dev;
|
|
len = skb->len;
|
|
ret = dev_queue_xmit(skb);
|
|
count_tx(dev, ret, len);
|
|
return ret;
|
|
}
|
|
|
|
if (!secy->operational) {
|
|
kfree_skb(skb);
|
|
dev->stats.tx_dropped++;
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
skb = macsec_encrypt(skb, dev);
|
|
if (IS_ERR(skb)) {
|
|
if (PTR_ERR(skb) != -EINPROGRESS)
|
|
dev->stats.tx_dropped++;
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa);
|
|
|
|
macsec_encrypt_finish(skb, dev);
|
|
len = skb->len;
|
|
ret = dev_queue_xmit(skb);
|
|
count_tx(dev, ret, len);
|
|
return ret;
|
|
}
|
|
|
|
#define SW_MACSEC_FEATURES \
|
|
(NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
|
|
static struct lock_class_key macsec_netdev_addr_lock_key;
|
|
|
|
/* If h/w offloading is enabled, use real device features save for
|
|
* VLAN_FEATURES - they require additional ops
|
|
* HW_MACSEC - no reason to report it
|
|
*/
|
|
#define REAL_DEV_FEATURES(dev) \
|
|
((dev)->features & ~(NETIF_F_VLAN_FEATURES | NETIF_F_HW_MACSEC))
|
|
|
|
static int macsec_dev_init(struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
const struct macsec_ops *ops;
|
|
int err;
|
|
|
|
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
|
if (!dev->tstats)
|
|
return -ENOMEM;
|
|
|
|
err = gro_cells_init(&macsec->gro_cells, dev);
|
|
if (err) {
|
|
free_percpu(dev->tstats);
|
|
return err;
|
|
}
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), NULL);
|
|
if (ops) {
|
|
dev->features = REAL_DEV_FEATURES(real_dev);
|
|
} else {
|
|
dev->features = real_dev->features & SW_MACSEC_FEATURES;
|
|
dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
|
|
}
|
|
|
|
dev->needed_headroom = real_dev->needed_headroom +
|
|
MACSEC_NEEDED_HEADROOM;
|
|
dev->needed_tailroom = real_dev->needed_tailroom +
|
|
MACSEC_NEEDED_TAILROOM;
|
|
|
|
if (is_zero_ether_addr(dev->dev_addr))
|
|
eth_hw_addr_inherit(dev, real_dev);
|
|
if (is_zero_ether_addr(dev->broadcast))
|
|
memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void macsec_dev_uninit(struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
|
|
gro_cells_destroy(&macsec->gro_cells);
|
|
free_percpu(dev->tstats);
|
|
}
|
|
|
|
static netdev_features_t macsec_fix_features(struct net_device *dev,
|
|
netdev_features_t features)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
const struct macsec_ops *ops;
|
|
|
|
ops = macsec_get_ops(netdev_priv(dev), NULL);
|
|
if (ops)
|
|
return REAL_DEV_FEATURES(real_dev);
|
|
|
|
features &= (real_dev->features & SW_MACSEC_FEATURES) |
|
|
NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES;
|
|
features |= NETIF_F_LLTX;
|
|
|
|
return features;
|
|
}
|
|
|
|
static int macsec_dev_open(struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
int err;
|
|
|
|
err = dev_uc_add(real_dev, dev->dev_addr);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (dev->flags & IFF_ALLMULTI) {
|
|
err = dev_set_allmulti(real_dev, 1);
|
|
if (err < 0)
|
|
goto del_unicast;
|
|
}
|
|
|
|
if (dev->flags & IFF_PROMISC) {
|
|
err = dev_set_promiscuity(real_dev, 1);
|
|
if (err < 0)
|
|
goto clear_allmulti;
|
|
}
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.secy = &macsec->secy;
|
|
err = macsec_offload(ops->mdo_dev_open, &ctx);
|
|
if (err)
|
|
goto clear_allmulti;
|
|
}
|
|
|
|
if (netif_carrier_ok(real_dev))
|
|
netif_carrier_on(dev);
|
|
|
|
return 0;
|
|
clear_allmulti:
|
|
if (dev->flags & IFF_ALLMULTI)
|
|
dev_set_allmulti(real_dev, -1);
|
|
del_unicast:
|
|
dev_uc_del(real_dev, dev->dev_addr);
|
|
netif_carrier_off(dev);
|
|
return err;
|
|
}
|
|
|
|
static int macsec_dev_stop(struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.secy = &macsec->secy;
|
|
macsec_offload(ops->mdo_dev_stop, &ctx);
|
|
}
|
|
|
|
dev_mc_unsync(real_dev, dev);
|
|
dev_uc_unsync(real_dev, dev);
|
|
|
|
if (dev->flags & IFF_ALLMULTI)
|
|
dev_set_allmulti(real_dev, -1);
|
|
|
|
if (dev->flags & IFF_PROMISC)
|
|
dev_set_promiscuity(real_dev, -1);
|
|
|
|
dev_uc_del(real_dev, dev->dev_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void macsec_dev_change_rx_flags(struct net_device *dev, int change)
|
|
{
|
|
struct net_device *real_dev = macsec_priv(dev)->real_dev;
|
|
|
|
if (!(dev->flags & IFF_UP))
|
|
return;
|
|
|
|
if (change & IFF_ALLMULTI)
|
|
dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1);
|
|
|
|
if (change & IFF_PROMISC)
|
|
dev_set_promiscuity(real_dev,
|
|
dev->flags & IFF_PROMISC ? 1 : -1);
|
|
}
|
|
|
|
static void macsec_dev_set_rx_mode(struct net_device *dev)
|
|
{
|
|
struct net_device *real_dev = macsec_priv(dev)->real_dev;
|
|
|
|
dev_mc_sync(real_dev, dev);
|
|
dev_uc_sync(real_dev, dev);
|
|
}
|
|
|
|
static sci_t dev_to_sci(struct net_device *dev, __be16 port)
|
|
{
|
|
return make_sci(dev->dev_addr, port);
|
|
}
|
|
|
|
static int macsec_set_mac_address(struct net_device *dev, void *p)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
const struct macsec_ops *ops;
|
|
struct macsec_context ctx;
|
|
struct sockaddr *addr = p;
|
|
int err;
|
|
|
|
if (!is_valid_ether_addr(addr->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
|
|
if (!(dev->flags & IFF_UP))
|
|
goto out;
|
|
|
|
err = dev_uc_add(real_dev, addr->sa_data);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
dev_uc_del(real_dev, dev->dev_addr);
|
|
|
|
out:
|
|
ether_addr_copy(dev->dev_addr, addr->sa_data);
|
|
|
|
macsec->secy.sci = dev_to_sci(dev, MACSEC_PORT_ES);
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.secy = &macsec->secy;
|
|
return macsec_offload(ops->mdo_upd_secy, &ctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int macsec_change_mtu(struct net_device *dev, int new_mtu)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
unsigned int extra = macsec->secy.icv_len + macsec_extra_len(true);
|
|
|
|
if (macsec->real_dev->mtu - extra < new_mtu)
|
|
return -ERANGE;
|
|
|
|
dev->mtu = new_mtu;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void macsec_get_stats64(struct net_device *dev,
|
|
struct rtnl_link_stats64 *s)
|
|
{
|
|
int cpu;
|
|
|
|
if (!dev->tstats)
|
|
return;
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
struct pcpu_sw_netstats *stats;
|
|
struct pcpu_sw_netstats tmp;
|
|
int start;
|
|
|
|
stats = per_cpu_ptr(dev->tstats, cpu);
|
|
do {
|
|
start = u64_stats_fetch_begin_irq(&stats->syncp);
|
|
tmp.rx_packets = stats->rx_packets;
|
|
tmp.rx_bytes = stats->rx_bytes;
|
|
tmp.tx_packets = stats->tx_packets;
|
|
tmp.tx_bytes = stats->tx_bytes;
|
|
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
|
|
|
|
s->rx_packets += tmp.rx_packets;
|
|
s->rx_bytes += tmp.rx_bytes;
|
|
s->tx_packets += tmp.tx_packets;
|
|
s->tx_bytes += tmp.tx_bytes;
|
|
}
|
|
|
|
s->rx_dropped = dev->stats.rx_dropped;
|
|
s->tx_dropped = dev->stats.tx_dropped;
|
|
}
|
|
|
|
static int macsec_get_iflink(const struct net_device *dev)
|
|
{
|
|
return macsec_priv(dev)->real_dev->ifindex;
|
|
}
|
|
|
|
|
|
static int macsec_get_nest_level(struct net_device *dev)
|
|
{
|
|
return macsec_priv(dev)->nest_level;
|
|
}
|
|
|
|
|
|
static const struct net_device_ops macsec_netdev_ops = {
|
|
.ndo_init = macsec_dev_init,
|
|
.ndo_uninit = macsec_dev_uninit,
|
|
.ndo_open = macsec_dev_open,
|
|
.ndo_stop = macsec_dev_stop,
|
|
.ndo_fix_features = macsec_fix_features,
|
|
.ndo_change_mtu = macsec_change_mtu,
|
|
.ndo_set_rx_mode = macsec_dev_set_rx_mode,
|
|
.ndo_change_rx_flags = macsec_dev_change_rx_flags,
|
|
.ndo_set_mac_address = macsec_set_mac_address,
|
|
.ndo_start_xmit = macsec_start_xmit,
|
|
.ndo_get_stats64 = macsec_get_stats64,
|
|
.ndo_get_iflink = macsec_get_iflink,
|
|
.ndo_get_lock_subclass = macsec_get_nest_level,
|
|
};
|
|
|
|
static const struct device_type macsec_type = {
|
|
.name = "macsec",
|
|
};
|
|
|
|
static const struct nla_policy macsec_rtnl_policy[IFLA_MACSEC_MAX + 1] = {
|
|
[IFLA_MACSEC_SCI] = { .type = NLA_U64 },
|
|
[IFLA_MACSEC_PORT] = { .type = NLA_U16 },
|
|
[IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 },
|
|
[IFLA_MACSEC_WINDOW] = { .type = NLA_U32 },
|
|
[IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_PROTECT] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_ES] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_SCB] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 },
|
|
[IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 },
|
|
};
|
|
|
|
static void macsec_free_netdev(struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
|
|
free_percpu(macsec->stats);
|
|
free_percpu(macsec->secy.tx_sc.stats);
|
|
|
|
}
|
|
|
|
static void macsec_setup(struct net_device *dev)
|
|
{
|
|
ether_setup(dev);
|
|
dev->min_mtu = 0;
|
|
dev->max_mtu = ETH_MAX_MTU;
|
|
dev->priv_flags |= IFF_NO_QUEUE;
|
|
dev->netdev_ops = &macsec_netdev_ops;
|
|
dev->needs_free_netdev = true;
|
|
dev->priv_destructor = macsec_free_netdev;
|
|
SET_NETDEV_DEVTYPE(dev, &macsec_type);
|
|
|
|
eth_zero_addr(dev->broadcast);
|
|
}
|
|
|
|
static void macsec_changelink_common(struct net_device *dev,
|
|
struct nlattr *data[])
|
|
{
|
|
struct macsec_secy *secy;
|
|
struct macsec_tx_sc *tx_sc;
|
|
|
|
secy = &macsec_priv(dev)->secy;
|
|
tx_sc = &secy->tx_sc;
|
|
|
|
if (data[IFLA_MACSEC_ENCODING_SA]) {
|
|
struct macsec_tx_sa *tx_sa;
|
|
|
|
tx_sc->encoding_sa = nla_get_u8(data[IFLA_MACSEC_ENCODING_SA]);
|
|
tx_sa = rtnl_dereference(tx_sc->sa[tx_sc->encoding_sa]);
|
|
|
|
secy->operational = tx_sa && tx_sa->active;
|
|
}
|
|
|
|
if (data[IFLA_MACSEC_WINDOW])
|
|
secy->replay_window = nla_get_u32(data[IFLA_MACSEC_WINDOW]);
|
|
|
|
if (data[IFLA_MACSEC_ENCRYPT])
|
|
tx_sc->encrypt = !!nla_get_u8(data[IFLA_MACSEC_ENCRYPT]);
|
|
|
|
if (data[IFLA_MACSEC_PROTECT])
|
|
secy->protect_frames = !!nla_get_u8(data[IFLA_MACSEC_PROTECT]);
|
|
|
|
if (data[IFLA_MACSEC_INC_SCI])
|
|
tx_sc->send_sci = !!nla_get_u8(data[IFLA_MACSEC_INC_SCI]);
|
|
|
|
if (data[IFLA_MACSEC_ES])
|
|
tx_sc->end_station = !!nla_get_u8(data[IFLA_MACSEC_ES]);
|
|
|
|
if (data[IFLA_MACSEC_SCB])
|
|
tx_sc->scb = !!nla_get_u8(data[IFLA_MACSEC_SCB]);
|
|
|
|
if (data[IFLA_MACSEC_REPLAY_PROTECT])
|
|
secy->replay_protect = !!nla_get_u8(data[IFLA_MACSEC_REPLAY_PROTECT]);
|
|
|
|
if (data[IFLA_MACSEC_VALIDATION])
|
|
secy->validate_frames = nla_get_u8(data[IFLA_MACSEC_VALIDATION]);
|
|
}
|
|
|
|
static int macsec_changelink(struct net_device *dev, struct nlattr *tb[],
|
|
struct nlattr *data[],
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct macsec_context ctx;
|
|
const struct macsec_ops *ops;
|
|
|
|
if (!data)
|
|
return 0;
|
|
|
|
if (data[IFLA_MACSEC_CIPHER_SUITE] ||
|
|
data[IFLA_MACSEC_ICV_LEN] ||
|
|
data[IFLA_MACSEC_SCI] ||
|
|
data[IFLA_MACSEC_PORT])
|
|
return -EINVAL;
|
|
|
|
macsec_changelink_common(dev, data);
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.secy = &macsec->secy;
|
|
return macsec_offload(ops->mdo_upd_secy, &ctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void macsec_del_dev(struct macsec_dev *macsec)
|
|
{
|
|
int i;
|
|
|
|
while (macsec->secy.rx_sc) {
|
|
struct macsec_rx_sc *rx_sc = rtnl_dereference(macsec->secy.rx_sc);
|
|
|
|
rcu_assign_pointer(macsec->secy.rx_sc, rx_sc->next);
|
|
free_rx_sc(rx_sc);
|
|
}
|
|
|
|
for (i = 0; i < MACSEC_NUM_AN; i++) {
|
|
struct macsec_tx_sa *sa = rtnl_dereference(macsec->secy.tx_sc.sa[i]);
|
|
|
|
if (sa) {
|
|
RCU_INIT_POINTER(macsec->secy.tx_sc.sa[i], NULL);
|
|
clear_tx_sa(sa);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void macsec_common_dellink(struct net_device *dev, struct list_head *head)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
|
|
unregister_netdevice_queue(dev, head);
|
|
list_del_rcu(&macsec->secys);
|
|
macsec_del_dev(macsec);
|
|
netdev_upper_dev_unlink(real_dev, dev);
|
|
|
|
macsec_generation++;
|
|
}
|
|
|
|
static void macsec_dellink(struct net_device *dev, struct list_head *head)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct net_device *real_dev = macsec->real_dev;
|
|
struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
|
|
struct macsec_context ctx;
|
|
const struct macsec_ops *ops;
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.secy = &macsec->secy;
|
|
macsec_offload(ops->mdo_del_secy, &ctx);
|
|
}
|
|
|
|
macsec_common_dellink(dev, head);
|
|
|
|
if (list_empty(&rxd->secys)) {
|
|
netdev_rx_handler_unregister(real_dev);
|
|
kfree(rxd);
|
|
}
|
|
}
|
|
|
|
static int register_macsec_dev(struct net_device *real_dev,
|
|
struct net_device *dev)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
|
|
|
|
if (!rxd) {
|
|
int err;
|
|
|
|
rxd = kmalloc(sizeof(*rxd), GFP_KERNEL);
|
|
if (!rxd)
|
|
return -ENOMEM;
|
|
|
|
INIT_LIST_HEAD(&rxd->secys);
|
|
|
|
err = netdev_rx_handler_register(real_dev, macsec_handle_frame,
|
|
rxd);
|
|
if (err < 0) {
|
|
kfree(rxd);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
list_add_tail_rcu(&macsec->secys, &rxd->secys);
|
|
return 0;
|
|
}
|
|
|
|
static bool sci_exists(struct net_device *dev, sci_t sci)
|
|
{
|
|
struct macsec_rxh_data *rxd = macsec_data_rtnl(dev);
|
|
struct macsec_dev *macsec;
|
|
|
|
list_for_each_entry(macsec, &rxd->secys, secys) {
|
|
if (macsec->secy.sci == sci)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct macsec_secy *secy = &macsec->secy;
|
|
|
|
macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats);
|
|
if (!macsec->stats)
|
|
return -ENOMEM;
|
|
|
|
secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats);
|
|
if (!secy->tx_sc.stats) {
|
|
free_percpu(macsec->stats);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (sci == MACSEC_UNDEF_SCI)
|
|
sci = dev_to_sci(dev, MACSEC_PORT_ES);
|
|
|
|
secy->netdev = dev;
|
|
secy->operational = true;
|
|
secy->key_len = DEFAULT_SAK_LEN;
|
|
secy->icv_len = icv_len;
|
|
secy->validate_frames = MACSEC_VALIDATE_DEFAULT;
|
|
secy->protect_frames = true;
|
|
secy->replay_protect = false;
|
|
|
|
secy->sci = sci;
|
|
secy->tx_sc.active = true;
|
|
secy->tx_sc.encoding_sa = DEFAULT_ENCODING_SA;
|
|
secy->tx_sc.encrypt = DEFAULT_ENCRYPT;
|
|
secy->tx_sc.send_sci = DEFAULT_SEND_SCI;
|
|
secy->tx_sc.end_station = false;
|
|
secy->tx_sc.scb = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int macsec_newlink(struct net *net, struct net_device *dev,
|
|
struct nlattr *tb[], struct nlattr *data[],
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct macsec_dev *macsec = macsec_priv(dev);
|
|
struct macsec_context ctx;
|
|
const struct macsec_ops *ops;
|
|
u8 icv_len = DEFAULT_ICV_LEN;
|
|
rx_handler_func_t *rx_handler;
|
|
u8 icv_len = DEFAULT_ICV_LEN;
|
|
struct net_device *real_dev;
|
|
int err, mtu;
|
|
sci_t sci;
|
|
|
|
if (!tb[IFLA_LINK])
|
|
return -EINVAL;
|
|
real_dev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK]));
|
|
if (!real_dev)
|
|
return -ENODEV;
|
|
if (real_dev->type != ARPHRD_ETHER)
|
|
return -EINVAL;
|
|
|
|
dev->priv_flags |= IFF_MACSEC;
|
|
|
|
macsec->real_dev = real_dev;
|
|
|
|
if (data && data[IFLA_MACSEC_ICV_LEN])
|
|
icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
|
|
mtu = real_dev->mtu - icv_len - macsec_extra_len(true);
|
|
if (mtu < 0)
|
|
dev->mtu = 0;
|
|
else
|
|
dev->mtu = mtu;
|
|
|
|
rx_handler = rtnl_dereference(real_dev->rx_handler);
|
|
if (rx_handler && rx_handler != macsec_handle_frame)
|
|
return -EBUSY;
|
|
|
|
err = register_netdevice(dev);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
macsec->nest_level = dev_get_nest_level(real_dev) + 1;
|
|
netdev_lockdep_set_classes(dev);
|
|
lockdep_set_class_and_subclass(&dev->addr_list_lock,
|
|
&macsec_netdev_addr_lock_key,
|
|
macsec_get_nest_level(dev));
|
|
|
|
err = netdev_upper_dev_link(real_dev, dev);
|
|
if (err < 0)
|
|
goto unregister;
|
|
|
|
/* need to be already registered so that ->init has run and
|
|
* the MAC addr is set
|
|
*/
|
|
if (data && data[IFLA_MACSEC_SCI])
|
|
sci = nla_get_sci(data[IFLA_MACSEC_SCI]);
|
|
else if (data && data[IFLA_MACSEC_PORT])
|
|
sci = dev_to_sci(dev, nla_get_be16(data[IFLA_MACSEC_PORT]));
|
|
else
|
|
sci = dev_to_sci(dev, MACSEC_PORT_ES);
|
|
|
|
if (rx_handler && sci_exists(real_dev, sci)) {
|
|
err = -EBUSY;
|
|
goto unlink;
|
|
}
|
|
|
|
err = macsec_add_dev(dev, sci, icv_len);
|
|
if (err)
|
|
goto unlink;
|
|
|
|
if (data)
|
|
macsec_changelink_common(dev, data);
|
|
|
|
/* If h/w offloading is available, propagate to the device */
|
|
ops = macsec_get_ops(netdev_priv(dev), &ctx);
|
|
if (ops) {
|
|
ctx.secy = &macsec->secy;
|
|
err = macsec_offload(ops->mdo_add_secy, &ctx);
|
|
if (err)
|
|
goto del_dev;
|
|
}
|
|
|
|
err = register_macsec_dev(real_dev, dev);
|
|
if (err < 0)
|
|
goto del_dev;
|
|
|
|
netif_stacked_transfer_operstate(real_dev, dev);
|
|
linkwatch_fire_event(dev);
|
|
|
|
macsec_generation++;
|
|
|
|
return 0;
|
|
|
|
del_dev:
|
|
macsec_del_dev(macsec);
|
|
unlink:
|
|
netdev_upper_dev_unlink(real_dev, dev);
|
|
unregister:
|
|
unregister_netdevice(dev);
|
|
return err;
|
|
}
|
|
|
|
static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[],
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u64 csid = MACSEC_DEFAULT_CIPHER_ID;
|
|
u8 icv_len = DEFAULT_ICV_LEN;
|
|
int flag;
|
|
bool es, scb, sci;
|
|
|
|
if (!data)
|
|
return 0;
|
|
|
|
if (data[IFLA_MACSEC_CIPHER_SUITE])
|
|
csid = nla_get_u64(data[IFLA_MACSEC_CIPHER_SUITE]);
|
|
|
|
if (data[IFLA_MACSEC_ICV_LEN]) {
|
|
icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
|
|
if (icv_len != DEFAULT_ICV_LEN) {
|
|
char dummy_key[DEFAULT_SAK_LEN] = { 0 };
|
|
struct crypto_aead *dummy_tfm;
|
|
|
|
dummy_tfm = macsec_alloc_tfm(dummy_key,
|
|
DEFAULT_SAK_LEN,
|
|
icv_len);
|
|
if (IS_ERR(dummy_tfm))
|
|
return PTR_ERR(dummy_tfm);
|
|
crypto_free_aead(dummy_tfm);
|
|
}
|
|
}
|
|
|
|
switch (csid) {
|
|
case MACSEC_DEFAULT_CIPHER_ID:
|
|
case MACSEC_DEFAULT_CIPHER_ALT:
|
|
if (icv_len < MACSEC_MIN_ICV_LEN ||
|
|
icv_len > MACSEC_STD_ICV_LEN)
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (data[IFLA_MACSEC_ENCODING_SA]) {
|
|
if (nla_get_u8(data[IFLA_MACSEC_ENCODING_SA]) >= MACSEC_NUM_AN)
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (flag = IFLA_MACSEC_ENCODING_SA + 1;
|
|
flag < IFLA_MACSEC_VALIDATION;
|
|
flag++) {
|
|
if (data[flag]) {
|
|
if (nla_get_u8(data[flag]) > 1)
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
es = data[IFLA_MACSEC_ES] ? nla_get_u8(data[IFLA_MACSEC_ES]) : false;
|
|
sci = data[IFLA_MACSEC_INC_SCI] ? nla_get_u8(data[IFLA_MACSEC_INC_SCI]) : false;
|
|
scb = data[IFLA_MACSEC_SCB] ? nla_get_u8(data[IFLA_MACSEC_SCB]) : false;
|
|
|
|
if ((sci && (scb || es)) || (scb && es))
|
|
return -EINVAL;
|
|
|
|
if (data[IFLA_MACSEC_VALIDATION] &&
|
|
nla_get_u8(data[IFLA_MACSEC_VALIDATION]) > MACSEC_VALIDATE_MAX)
|
|
return -EINVAL;
|
|
|
|
if ((data[IFLA_MACSEC_REPLAY_PROTECT] &&
|
|
nla_get_u8(data[IFLA_MACSEC_REPLAY_PROTECT])) &&
|
|
!data[IFLA_MACSEC_WINDOW])
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct net *macsec_get_link_net(const struct net_device *dev)
|
|
{
|
|
return dev_net(macsec_priv(dev)->real_dev);
|
|
}
|
|
|
|
static size_t macsec_get_size(const struct net_device *dev)
|
|
{
|
|
return nla_total_size_64bit(8) + /* IFLA_MACSEC_SCI */
|
|
nla_total_size(1) + /* IFLA_MACSEC_ICV_LEN */
|
|
nla_total_size_64bit(8) + /* IFLA_MACSEC_CIPHER_SUITE */
|
|
nla_total_size(4) + /* IFLA_MACSEC_WINDOW */
|
|
nla_total_size(1) + /* IFLA_MACSEC_ENCODING_SA */
|
|
nla_total_size(1) + /* IFLA_MACSEC_ENCRYPT */
|
|
nla_total_size(1) + /* IFLA_MACSEC_PROTECT */
|
|
nla_total_size(1) + /* IFLA_MACSEC_INC_SCI */
|
|
nla_total_size(1) + /* IFLA_MACSEC_ES */
|
|
nla_total_size(1) + /* IFLA_MACSEC_SCB */
|
|
nla_total_size(1) + /* IFLA_MACSEC_REPLAY_PROTECT */
|
|
nla_total_size(1) + /* IFLA_MACSEC_VALIDATION */
|
|
0;
|
|
}
|
|
|
|
static int macsec_fill_info(struct sk_buff *skb,
|
|
const struct net_device *dev)
|
|
{
|
|
struct macsec_secy *secy = &macsec_priv(dev)->secy;
|
|
struct macsec_tx_sc *tx_sc = &secy->tx_sc;
|
|
|
|
if (nla_put_sci(skb, IFLA_MACSEC_SCI, secy->sci,
|
|
IFLA_MACSEC_PAD) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_ICV_LEN, secy->icv_len) ||
|
|
nla_put_u64_64bit(skb, IFLA_MACSEC_CIPHER_SUITE,
|
|
MACSEC_DEFAULT_CIPHER_ID, IFLA_MACSEC_PAD) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_ENCODING_SA, tx_sc->encoding_sa) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_ENCRYPT, tx_sc->encrypt) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_PROTECT, secy->protect_frames) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_INC_SCI, tx_sc->send_sci) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_ES, tx_sc->end_station) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_SCB, tx_sc->scb) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_REPLAY_PROTECT, secy->replay_protect) ||
|
|
nla_put_u8(skb, IFLA_MACSEC_VALIDATION, secy->validate_frames) ||
|
|
0)
|
|
goto nla_put_failure;
|
|
|
|
if (secy->replay_protect) {
|
|
if (nla_put_u32(skb, IFLA_MACSEC_WINDOW, secy->replay_window))
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static struct rtnl_link_ops macsec_link_ops __read_mostly = {
|
|
.kind = "macsec",
|
|
.priv_size = sizeof(struct macsec_dev),
|
|
.maxtype = IFLA_MACSEC_MAX,
|
|
.policy = macsec_rtnl_policy,
|
|
.setup = macsec_setup,
|
|
.validate = macsec_validate_attr,
|
|
.newlink = macsec_newlink,
|
|
.changelink = macsec_changelink,
|
|
.dellink = macsec_dellink,
|
|
.get_size = macsec_get_size,
|
|
.fill_info = macsec_fill_info,
|
|
.get_link_net = macsec_get_link_net,
|
|
};
|
|
|
|
static bool is_macsec_master(struct net_device *dev)
|
|
{
|
|
return rcu_access_pointer(dev->rx_handler) == macsec_handle_frame;
|
|
}
|
|
|
|
static int macsec_notify(struct notifier_block *this, unsigned long event,
|
|
void *ptr)
|
|
{
|
|
struct net_device *real_dev = netdev_notifier_info_to_dev(ptr);
|
|
LIST_HEAD(head);
|
|
|
|
if (!is_macsec_master(real_dev))
|
|
return NOTIFY_DONE;
|
|
|
|
switch (event) {
|
|
case NETDEV_DOWN:
|
|
case NETDEV_UP:
|
|
case NETDEV_CHANGE: {
|
|
struct macsec_dev *m, *n;
|
|
struct macsec_rxh_data *rxd;
|
|
|
|
rxd = macsec_data_rtnl(real_dev);
|
|
list_for_each_entry_safe(m, n, &rxd->secys, secys) {
|
|
struct net_device *dev = m->secy.netdev;
|
|
|
|
netif_stacked_transfer_operstate(real_dev, dev);
|
|
}
|
|
break;
|
|
}
|
|
case NETDEV_UNREGISTER: {
|
|
struct macsec_dev *m, *n;
|
|
struct macsec_rxh_data *rxd;
|
|
|
|
rxd = macsec_data_rtnl(real_dev);
|
|
list_for_each_entry_safe(m, n, &rxd->secys, secys) {
|
|
macsec_common_dellink(m->secy.netdev, &head);
|
|
}
|
|
|
|
netdev_rx_handler_unregister(real_dev);
|
|
kfree(rxd);
|
|
|
|
unregister_netdevice_many(&head);
|
|
break;
|
|
}
|
|
case NETDEV_CHANGEMTU: {
|
|
struct macsec_dev *m;
|
|
struct macsec_rxh_data *rxd;
|
|
|
|
rxd = macsec_data_rtnl(real_dev);
|
|
list_for_each_entry(m, &rxd->secys, secys) {
|
|
struct net_device *dev = m->secy.netdev;
|
|
unsigned int mtu = real_dev->mtu - (m->secy.icv_len +
|
|
macsec_extra_len(true));
|
|
|
|
if (dev->mtu > mtu)
|
|
dev_set_mtu(dev, mtu);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block macsec_notifier = {
|
|
.notifier_call = macsec_notify,
|
|
};
|
|
|
|
static int __init macsec_init(void)
|
|
{
|
|
int err;
|
|
|
|
pr_info("MACsec IEEE 802.1AE\n");
|
|
err = register_netdevice_notifier(&macsec_notifier);
|
|
if (err)
|
|
return err;
|
|
|
|
err = rtnl_link_register(&macsec_link_ops);
|
|
if (err)
|
|
goto notifier;
|
|
|
|
err = genl_register_family(&macsec_fam);
|
|
if (err)
|
|
goto rtnl;
|
|
|
|
return 0;
|
|
|
|
rtnl:
|
|
rtnl_link_unregister(&macsec_link_ops);
|
|
notifier:
|
|
unregister_netdevice_notifier(&macsec_notifier);
|
|
return err;
|
|
}
|
|
|
|
static void __exit macsec_exit(void)
|
|
{
|
|
genl_unregister_family(&macsec_fam);
|
|
rtnl_link_unregister(&macsec_link_ops);
|
|
unregister_netdevice_notifier(&macsec_notifier);
|
|
rcu_barrier();
|
|
}
|
|
|
|
module_init(macsec_init);
|
|
module_exit(macsec_exit);
|
|
|
|
MODULE_ALIAS_RTNL_LINK("macsec");
|
|
MODULE_ALIAS_GENL_FAMILY("macsec");
|
|
|
|
MODULE_DESCRIPTION("MACsec IEEE 802.1AE");
|
|
MODULE_LICENSE("GPL v2");
|