Lenky个人站点 http://www.lenky.info 色不异空 . 空不异色 . 色即是空 . 空即是色 Sun, 30 Dec 2018 03:41:22 +0000 zh-CN hourly 1 http://wordpress.org/?v=4.0.9 kvm网卡透传 http://www.lenky.info/archives/2018/12/2667 http://www.lenky.info/archives/2018/12/2667#comments Sun, 30 Dec 2018 03:41:22 +0000 http://www.lenky.info/?p=2667 一,KVM里CentOS 7虚拟机的网络配置
1,宿主机是CentOS 7,KVM虚拟机也是CentOS 7

2,在宿主机上有物理网卡eth0,配置有ip:192.168.1.2/24
有网桥virbr0,配置有ip:192.168.122.1/24
该virbr0绑定在virbr0-nic接口上,而virbr0-nic貌似是一个tun/tap设备,因此性能非常差。
所有KVM虚拟机挂在这个virbr0网桥上,然后通过virbr0-nic进行相互通信。
如果虚拟机要访问外部主机,则通过virbr0-nic做NAT出去。
如果外部主机要访问虚拟机,则比较麻烦,或许可能不行。
# brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.52540098e452 yes virbr0-nic
vnet0
vnet0是KVM虚拟机网卡在宿主机上对应的tap设备,如果还有其他KVM虚拟机,则都接到这个virbr0。

3,在上一步中,看到网桥是绑定在virbr0-nic虚拟设备上的,其实可以直接绑定在宿主机的物理网卡eth0上。
a, 新增网桥br0以及配置
# brctl addbr br0
# vi /etc/sysconfig/network-scripts/ifcfg-br0
# cat /etc/sysconfig/network-scripts/ifcfg-br0
TYPE=bridge
BOOTPROTO=none

IPADDR=192.168.1.205
NETMASK=255.255.0.0
GATEWAY=192.168.1.254
NM_CONTROLLED=no

DEFROUTE=yes
PEERDNS=yes

IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no

NAME=br0
DEVICE=br0
ONBOOT=yes

b, 修改eth0网卡配置,在最后一行加上BRIDGE=br0,表示将eth0桥接到br0:
# cat /etc/sysconfig/network-scripts/ifcfg-enp4s0f0
TYPE=Ethernet
BOOTPROTO=static

IPADDR=192.168.1.208
NETMASK=255.255.0.0
GATEWAY=192.168.1.254
NM_CONTROLLED=no

DEFROUTE=yes
PEERDNS=yes

IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no

NAME=enp4s0f0
DEVICE=enp4s0f0
ONBOOT=yes

BRIDGE=br0

c, 重启网络,务必多执行一次start,因为eth0启用依赖于br0,有可能第一次启动会失败。如果恰好eth0是用来做远程的,则可能导致网络断掉。
# service network restart; service network start;

把启动的虚拟机在宿主机上的对应网卡绑定到这个br0上:
# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000d48064198 no enp6s0f0
virbr0 8000.52540098e452 yes virbr0-nic
vnet0
# brctl delif virbr0 vnet0
# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000d48064198 no enp6s0f0
virbr0 8000.52540098e452 yes virbr0-nic
# brctl addif br0 vnet0
# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000d48064198 no enp6s0f0
vnet0
virbr0 8000.52540098e452 yes virbr0-nic

这种设置的虚拟机网络性能相比上一种要好,而性能更好的设置方式就是直接把物理网卡pass through到虚拟机。

4,做物理网卡pass through需要宿主机的硬件支持和一些准备工作
a, 确认宿主机的硬件支持,主要是cpu和主板,这可以查看官方的硬件支持列表,或者在BIOS中查看相关选项。以Intel硬件为例,主要就是:
VT-x:处理器技术,提供内存以及虚拟机的硬件隔离,所涉及的技术有页表管理以及地址空间的保护。
VT-d:处理有关芯片组的技术,它提供一些针对虚拟机的特殊应用,如支持某些特定的虚拟机应用跨过处理器I/O管理程序,直接调用I/O资源,从而提高效率,通过直接连接I/O带来近乎完美的I/O性能。
VT-c:针对网络提供的管理,它可以在一个物理网卡上,建立针对虚拟机的设备队列。
VT-c是后面将提到的SR-IOV关系比较大,本小节只需验证VT-x和VT-d,一般在BIOS中Advanced下CPU和System或相关条目中设置,都设置为Enabled:
VT:Intel Virtualization Technology
VT-d:Intel VT for Directed I/O
VT-c:I/O Virtualization

b, 修改内核启动参数,使IOMMU生效,CentOS7上修改稍微不同:
# cat /etc/default/grub

GRUB_CMDLINE_LINUX=”crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet intel_iommu=on”

在GRUB_CMDLINE_LINUX后加上intel_iommu=on,其他的不动。先备份,再重新生成grub.cfg:
# cp /boot/grub2/grub.cfg ~/grub.cfg.bak
# grub2-mkconfig -o /boot/grub2/grub.cfg
# diff /boot/grub2/grub.cfg ~/grub.cfg.bak
可以diff比较一下参数是否正确加上。

重启机器后执行如下两条命令进行确认:
# find /sys/kernel/iommu_groups/ -type l
# dmesg | grep -e DMAR -e IOMMU

如果有输出,那就说明ok了。如果没有,那再验证BIOS、内核编译项、内核启动参数等是否没有正确配置。比如内核是否已经编译了IOMMO:
# cat /boot/config-3.10.0-862.el7.x86_64 |grep IOMMU
CONFIG_GART_IOMMU=y
# CONFIG_CALGARY_IOMMU is not set
CONFIG_IOMMU_HELPER=y
CONFIG_VFIO_IOMMU_TYPE1=m
CONFIG_VFIO_NOIOMMU=y
CONFIG_IOMMU_API=y
CONFIG_IOMMU_SUPPORT=y
CONFIG_IOMMU_IOVA=y
CONFIG_AMD_IOMMU=y
CONFIG_AMD_IOMMU_V2=m
CONFIG_INTEL_IOMMU=y

c, 找一个没用的网卡设备,因为pass through是虚拟机独占,所以肯定不能用远程ip所对应的网卡设备,否则远程网络就断了。比如我的远程网卡为enp4s0f0,那么我这里拿enp8s0f0作为pass through网卡。

通过ethtool查看网卡的bus信息:
# ethtool -i enp8s0f0 | grep bus
bus-info: 0000:08:00.0

解除绑定(注意里面的0000:08:00.0是上一步获得的bus信息):
# lspci -s 0000:08:00.0 -n
08:00.0 0200: 8086:10c9 (rev 01)
# modprobe pci_stub
# echo 0000:08:00.0 > /sys/bus/pci/devices/0000\:08\:00.0/driver/unbind
# echo “8086 10c9″ > /sys/bus/pci/drivers/pci-stub/new_id

驱动确认(注意里面的:Kernel driver in use: pci-stub):
# lspci -s 0000:08:00.0 -k
08:00.0 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)
Subsystem: Intel Corporation Device 0000
Kernel driver in use: pci-stub
Kernel modules: igb

启动虚拟机:
kvm -name centos7 -smp 4 -m 8192 \
-drive file=/home/vmhome/centos7.qcow2,if=virtio,media=disk,index=0,format=qcow2 \
-drive file=/home/lenky/CentOS-7-x86_64-DVD-1804.iso,media=cdrom,index=1 \
-nographic -vnc :2 \
-net none -device pci-assign,host=0000:08:00.0

注意最后两个参数:
‘-net none’:告诉qemu不用模拟网卡设备
‘-device pci-assign,host=0000:08:00.0’:直接指定一个pci设备,对应的地址为宿主机上pci地址0000:08:00.0

执行上面命令,我这里出现一个错误:
kvm: -device pci-assign,host=0000:08:00.0: No IOMMU found. Unable to assign device “(null)”
kvm: -device pci-assign,host=0000:08:00.0: Device initialization failed.
kvm: -device pci-assign,host=0000:08:00.0: Device ‘kvm-pci-assign’ could not be initialized

然后我前面的配置都ok啊,经过搜索,问题在于最新的内核里,已建议废除KVM_ASSIGN机制,而只支持vfio,我这里查看CentOS 7的内核编译选项也果真如此:
# cat /boot/config-3.10.0-862.el7.x86_64 | grep KVM_DEVICE
# CONFIG_KVM_DEVICE_ASSIGNMENT is not set

所以换用vfio驱动。VFIO可以用于实现高效的用户态驱动。在虚拟化场景可以用于device passthrough。通过用户态配置IOMMU接口,可以将DMA地址空间映射限制在进程虚拟空间中。这对高性能驱动和虚拟化场景device passthrough尤其重要。相对于传统方式,VFIO对UEFI支持更好。VFIO技术实现了用户空间直接访问设备。无须root特权,更安全,功能更多。

重新解除绑定和再绑定:
# modprobe vfio
# modprobe vfio-pci
# lspci -s 0000:08:00.0 -n
08:00.0 0200: 8086:10c9 (rev 01)
# echo “8086 10c9″ > /sys/bus/pci/drivers/vfio-pci/new_id
# echo 0000:08:00.0 > /sys/bus/pci/devices/0000\:08\:00.0/driver/unbind
# echo 0000:08:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
# lspci -s 0000:08:00.0 -k
08:00.0 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)
Subsystem: Intel Corporation Device 0000
Kernel driver in use: vfio-pci
Kernel modules: igb

启动虚拟机:
kvm -name centos7 -smp 4 -m 8192 \
-drive file=/home/vmhome/centos7.qcow2,if=virtio,media=disk,index=0,format=qcow2 \
-drive file=/home/lenky/CentOS-7-x86_64-DVD-1804.iso,media=cdrom,index=1 \
-nographic -vnc :2 \
-net none -device vfio-pci,host=0000:08:00.0

这次一切OK,顺利启动并进入到CentOS 7虚拟机。

https://blog.csdn.net/leoufung/article/details/52144687

https://www.linux-kvm.org/page/10G_NIC_performance:_VFIO_vs_virtio

http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM

5,虚拟机独占物理网卡总是资源浪费,而且如果虚拟机比较多,又到哪有找那么多物理网卡。因此为了实现多个虚机共享一个物理设备,并且达到直接分配的目的,PCI-SIG组织发布了SR-IOV(Single Root I/O Virtualization and sharing)规范,它定义了一个标准化的机制用以原生地支持实现多个客户机共享一个设备。当前SR-IOV(单根I/O虚拟化)最广泛地应用还是网卡上。
SR-IOV使得一个单一的功能单元(比如,一个以太网端口)能看起来像多个独立的物理设备。一个带有SR-IOV功能的物理设备能被配置为多个功能单元。
SR-IOV使用两种功能(function):
物理功能(Physical Functions,PF):这是完整的带有SR-IOV能力的PCIe设备。PF能像普通PCI设备那样被发现、管理和配置。
虚拟功能(Virtual Functions,VF):简单的PCIe功能,它只能处理I/O。每个VF都是从PF中分离出来的。每个物理硬件都有一个VF数目的限制。一个PF,能被虚拟成多个VF用于分配给多个虚拟机。
Hypervisor能将一个或者多个VF分配给一个虚机。在某一时刻,一个VF只能被分配给一个虚机。一个虚机可以拥有多个VF。在虚机的操作系统看来,一个VF网卡看起来和一个普通网卡没有区别。SR-IOV驱动是在内核中实现的。

RedHat Linux 6.0官方只完整测试了下面的几款SR-IOV网卡:
Intel® 82576NS Gigabit Ethernet Controller (igb 驱动)
Intel® 82576EB Gigabit Ethernet Controller (igb 驱动)
Intel® 82599ES 10 Gigabit Ethernet Controller (ixgbe 驱动)
Intel® 82599EB 10 Gigabit Ethernet Controller (ixgbe 驱动)

a, 检查设备是否支持SR-IOV:
# lspci -s 0000:08:00.0 -vvv | grep -i “Single Root I/O Virtualization”
Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV)

看来我这个设备上的这个网卡是支持的。

b, 重新绑定到igb驱动:
# echo 0000:08:00.0 > /sys/bus/pci/devices/0000\:08\:00.0/driver/unbind
# echo “8086 10c9″ > /sys/bus/pci/drivers/igb/new_id
bash: echo: write error: File exists
# echo “8086 10c9″ > /sys/bus/pci/drivers/igb/bind
bash: echo: write error: No such device

出现上面这些错误,当前还不知道怎么回事,可能是因为我关闭kvm都是直接在宿主机里kill掉进程的,导致bus信息未释放?待进一步分析。

# echo igb > /sys/bus/pci/devices/0000\:08\:00.0/driver_override
# echo 0000:08:00.0 > /sys/bus/pci/drivers_probe
# lspci -s 0000:08:00.0 -k
08:00.0 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)
Subsystem: Intel Corporation Device 0000
Kernel driver in use: igb
Kernel modules: igb

c, 创建VF,可以通过重新加载内核模块参数来创建VF:
# modprobe -r igb; modprobe igb max_vfs=7

如果远程网卡也是用的igb,则会导致断网。因此还是直接只对0000:08:00.0网卡开启VF:
# lspci -nn | grep “Virtual Function”
# echo 2 > /sys/bus/pci/devices/0000\:08\:00.0/sriov_numvfs
# lspci -nn | grep “Virtual Function”
08:10.0 Ethernet controller [0200]: Intel Corporation 82576 Virtual Function [8086:10ca] (rev 01)
08:10.2 Ethernet controller [0200]: Intel Corporation 82576 Virtual Function [8086:10ca] (rev 01)
# echo 0 > /sys/bus/pci/devices/0000\:08\:00.0/sriov_numvfs
# lspci -nn | grep “Virtual Function”

也就是对sriov_numvfs进行数字写入,表示创建几个VF,写入0则删除所有VF。
如果要重启生效,那还是在模块加载时指定参数:
# echo “options igb max_vfs=2″ >>/etc/modprobe.d/igb.conf

d, 接下来就可以把VF当做普通网卡给虚拟机独占使用了
# lshw -c network -businfo
Bus info Device Class Description
========================================================

pci@0000:08:10.0 enp8s16 network 82576 Virtual Function
pci@0000:08:10.2 enp8s16f2 network 82576 Virtual Function

# ethtool -i enp8s16 | grep bus
bus-info: 0000:08:10.0
# lspci -s 0000:08:10.0 -n
08:10.0 0200: 8086:10ca (rev 01)
# modprobe vfio
# modprobe vfio-pci
# echo 0000:08:10.0 > /sys/bus/pci/devices/0000\:08\:10.0/driver/unbind
# echo “8086 10ca” > /sys/bus/pci/drivers/vfio-pci/new_id
# echo 0000:08:10.0 > /sys/bus/pci/drivers/vfio-pci/bind
# lspci -s 0000:08:10.0 -k
08:10.0 Ethernet controller: Intel Corporation 82576 Virtual Function (rev 01)
Subsystem: Intel Corporation Device 0000
Kernel driver in use: vfio-pci
Kernel modules: igbvf
# kvm -name centos7 -smp 4 -m 8192 \
-drive file=/home/vmhome/centos7.qcow2,if=virtio,media=disk,index=0,format=qcow2 \
-drive file=/home/lenky/CentOS-7-x86_64-DVD-1804.iso,media=cdrom,index=1 \
-nographic -vnc :2 \
-net none -device vfio-pci,host=0000:08:10.0

进入虚拟机后查看网卡的驱动信息,可以看到是用的igbvf:
# ethtool -i eth0
driver: igbvf
version: 2.4.0-k

5,pass through的麻烦之处在于需要指定具体的pci地址,比较麻烦,比如在虚拟机要做迁移的场景。
因此另外一种据说性能也非常好的方式是通过Virtio网卡。首先需要在内核打开如下选项:
CONFIG_VIRTIO=m
CONFIG_VIRTIO_RING=m
CONFIG_VIRTIO_PCI=m
CONFIG_VIRTIO_BALLOON=m
CONFIG_VIRTIO_BLK=m
CONFIG_VIRTIO_NET=m

CentOS 7自带内核默认已经打开,因此可以直接使用。
# cat /boot/config-3.10.0-862.el7.x86_64 | grep VIRTIO
CONFIG_VIRTIO_VSOCKETS=m
CONFIG_VIRTIO_VSOCKETS_COMMON=m
CONFIG_VIRTIO_BLK=m
CONFIG_SCSI_VIRTIO=m
CONFIG_VIRTIO_NET=m
CONFIG_VIRTIO_CONSOLE=m
CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_DRM_VIRTIO_GPU=m
CONFIG_VIRTIO=m
CONFIG_VIRTIO_PCI=m
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BALLOON=m
CONFIG_VIRTIO_INPUT=m
# CONFIG_VIRTIO_MMIO is not set

执行kvm:
kvm -name centos7 -smp 4 -m 8192 \
-drive file=/home/vmhome/centos7.qcow2,if=virtio,media=disk,index=0,format=qcow2 \
-drive file=/home/lenky/CentOS-7-x86_64-DVD-1804.iso,media=cdrom,index=1 \
-nographic -vnc :2 \
-device virtio-net-pci,netdev=net0 -netdev tap,id=net0,script=/home/vmhome/qemu-ifup,downscript=no

注意最后一行的网卡设置:
-device virtio-net-pci:指定了一个使用virtio-net-pci的设备,对应
,netdev=net0:和后面的id=net0关联起来,net0是任意值,只要一致就可以。
-netdev tap,id=net0,script=/home/vmhome/qemu-ifup,downscript=no:宿主机上对应桥接到交换机上的端口

进入虚拟机,查看网卡驱动,可以看到如下:
# ethtool -i eth0
driver: virtio_net
version: 1.0.0

根据注1,如果采用如下命令,性能会非常差:
kvm -name centos7 -smp 4 -m 8192 \
-drive file=/home/vmhome/centos7.qcow2,if=virtio,media=disk,index=0,format=qcow2 \
-drive file=/home/lenky/CentOS-7-x86_64-DVD-1804.iso,media=cdrom,index=1 \
-nographic -vnc :2 \
-net nic,model=virtio -net tap,script=/home/vmhome/qemu-ifup,downscript=no

但是根据注2,这两种写法应该是一样的,只不过-net nic,model=virtio是旧语法(old -net..-net syntax),实践验证后一种kvm启动的虚拟机里通过ethtool查看网卡的驱动也是virtio_net。难道是另外的某些原因还不得而知。

ps:通过如下命令可以查看当前qemu支持的网卡类型
# kvm -net nic,model=?
qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio

注:
1,https://www.linux-kvm.org/page/10G_NIC_performance:_VFIO_vs_virtio
2,http://www.linux-kvm.org/page/Virtio
3,https://www.cnblogs.com/sammyliu/p/4548194.html
4,https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/virtualization_host_configuration_and_guest_installation_guide/sect-virtualization_host_configuration_and_guest_installation_guide-sr_iov-how_sr_iov_libvirt_works
5,https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/pci/pci-driver.c?h=v3.16&id=782a985d7af26db39e86070d28f987cad21313c0

]]>
http://www.lenky.info/archives/2018/12/2667/feed 0
u盘安装CentOS,远程接入kvm等 http://www.lenky.info/archives/2018/12/2665 http://www.lenky.info/archives/2018/12/2665#comments Sun, 30 Dec 2018 03:39:15 +0000 http://www.lenky.info/?p=2665 一,U盘安装CentOS 7

1,准备:
Win8.1系统
U盘一个

2,在Win8.1系统上制作U盘启动盘
从CentOS官网下载CentOS7镜像:CentOS-7-x86_64-DVD-1804.iso

在Win8.1系统上安装UltrISO后,运行UltrISO;
通过“文件”—>“打开”选择CentOS镜像。
通过“启动”—>“写入硬盘映像”;
在弹出窗口里,硬盘驱动器选择U盘、勾选“刻录校验”,写入方式为USB-HDD+;隐藏启动分区选“无”;
点击“写入”,等待结束后,CentOS的U盘启动盘就做好了;

3,安装系统
a,把U盘启动盘插到对应CentOS安装设备上,BIOS选择U盘为第一引导。
b,安装过程中出现下面错误:
dracut-initqueue[624]:Warning: Could not boot.
dracut-initqueue[624]:Warning: /dev/root does not exist.
Starting Dracut EmergencyShell…
Warning: /dev/root does not exist

原因是:UltroISO在Window下写入U盘的安装文件路径,没有被linux安装程序识别
解决方法如下,在安装发生错误后会进入到一个命令行,在这个命令行里可以验证一下哪个是我们的U盘:
dracut:/# cd /dev
dracut:/dev# ls sd*

一般会有几个,比如sda、sda4、sdb、sdc等,可以尝试mount看看内容确认哪个磁盘是U盘,或者多尝试下面的内容几次也行:

重启在安装界面按下Tab键,修改启动配置,将:
vmlinuz initrd=initrd.img inst.stage2=hd:LABEL=CentOS\x207\x20x86_64 rd.live.check quiet
改为:
vmlinuz initrd=initrd.img inst.stage2=hd:/dev/sdb4 quiet
接下来安装就不多说了。

=============================分割线=============================

二,利用iso作为软件包安装源
1,准备iso镜像,比如:
CentOS-7-x86_64-DVD-1804.iso 或 CentOS-7-x86_64-Everything-1804.iso

2,加载镜像
# mkdir /mnt/cdrom
# mount -o loop /home/lenky/CentOS-7-x86_64-DVD-1804.iso /mnt/cdrom

3,修改软件包安装源
# cd /etc/yum.repos.d/
# mv CentOS-Base.repo CentOS-Base.repo.bak
# cp CentOS-Media.repo CentOS-Media.repo.bak
# vi CentOS-Media.repo
内容如下:
[c7-media]
name=CentOS-$releasever – Media
baseurl=file:///mnt/cdrom/
#baseurl=file:///media/CentOS/
# file:///media/cdrom/
# file:///media/cdrecorder/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

4,重置源缓存
# yum clean all
# rm -fr /var/cache/yum/
# yum list
# yum install tree

=============================分割线=============================

三,开机关闭图形界面

新版本的CentOS系统里使用targets取代原运行级别的概念。
系统有两种默认的targets:多用户.target对应之前版本的3运行级别;而图形.target对应之前的5运行级别。

查看默认的target,执行:
systemctl get-default

开机以命令模式启动,执行:
systemctl set-default multi-user.target

开机以图形界面启动,执行:
systemctl set-default graphical.target

=============================分割线=============================

四,Windows 8.1通过vnc远程CentOS 7
1,安装vnc-server
# yum install -y tigervnc-server

2,关闭selinux
# vi /etc/sysconfig/selinux
SELINUX=disabled

3,配置vnc-server
# cd /lib/systemd/system
# cp vncserver@.service vncserver@:1.service
# vi vncserver@\:1.service
a, 将里面的改为对应的用户名,比如tom,如果是root,注意PIDFile=/home//则为/root/
b, 将Type=forking改为Type=simple
c, 如果是root用户,需要修改xstartup的权限:chmod 777 /root/.vnc/xstartup,否则可能会导致远程接入后黑屏
# systemctl daemon-reload
# systemctl enable vncserver@:1.service
Created symlink from /etc/systemd/system/multi-user.target.wants/vncserver@:1.service to /usr/lib/systemd/system/vncserver@:1.service.

4,关闭防火墙
# service firewalld stop

5,设置vnc密码:先切换到tom账号,再通过vncpasswd设置密码
# su tom
$ vncpasswd
Password:
Verify:
Would you like to enter a view-only password (y/n)? y
Password:
Verify:

6,启动vnc
$ su //先切回root账号
# systemctl start vncserver@:1.service
# systemctl status vncserver@:1.service
● vncserver@:1.service – Remote desktop service (VNC)
Loaded: loaded (/usr/lib/systemd/system/vncserver@:1.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2018-07-27 21:33:46 EDT; 1min 33s ago

看到active (running)就ok了。

7,Windows 8.1上安装
vncserver监听端口有多个:
RFB(Remote FrameBuffer)协议 默认端口 : 5900 显示器号
HTTP协议默认端口 : 5800 显示器号
X协议 默认端口 : 6000 显示器号
vncserver使用的显示器编号默认从1开始, 依次使用, 也可以参数指定端口号。
# netstat -natp | grep vnc
tcp 0 0 0.0.0.0:5901 0.0.0.0:* LISTEN 12220/Xvnc
tcp 0 0 0.0.0.0:6001 0.0.0.0:* LISTEN 12220/Xvnc
tcp 0 0 199.201.91.208:5901 199.200.152.7:59620 ESTABLISHED 12220/Xvnc
tcp6 0 0 :::5901 :::* LISTEN 12220/Xvnc
tcp6 0 0 :::6001 :::*
我这里只使用RFB协议。
下载RealVNC(WinVNC)或tigervnc64-1.9.0.exe:https://github.com/TigerVNC/tigervnc/releases
安装完后,连接对应的ip:port,比如我这里是:192.168.1.2:5901
然后在弹出框里输入前面设置的密码,即可远程登录CentOS。

=============================分割线=============================

五,CentOS 7上命令行使用KVM
1,检查CPU对KVM的支持
# egrep ‘vmx|svm’ /proc/cpuinfo

2,安装KVM
# yum -y install qemu-kvm libvirt bridge-utils

3,关闭selinux
# vi /etc/sysconfig/selinux
SELINUX=disabled

4,重启机器后确保kvm已启用
# reboot
# lsmod | grep kvm
kvm_intel 174841 0
kvm 578518 1 kvm_intel
irqbypass 13503 1 kvm
# ls /dev/kvm -l
crw-rw-rw-+ 1 root kvm 10, 232 Jul 26 08:26 /dev/kvm

5,从CentOS 6开始,RedHat推荐使用virt-install/virsh系列工具操作 kvm,而不是直接使用qemu-kvm,所以qemu-kvm被移到一个不起眼的地方 /usr/libexec/,做个链接:
# qemu-kvm
bash: qemu-kvm: command not found…
# ls /usr/libexec/qemu-kvm
/usr/libexec/qemu-kvm
# ln -sf /usr/libexec/qemu-kvm /usr/bin/kvm

至此,CentOS 7上就能使用命令行KVM了。

=============================分割线=============================

六,远程到CentOS 7上使用KVM命令行安装CentOS 7虚拟机
1,在Windows 8.1上利用xshell远程到CentOS 7物理机器

2,确保网桥已经配置,转发已经开启(我这里已经配置好了,因为已经安装了libvirt服务)
# yum install bridge-utils
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
Package bridge-utils-1.5-9.el7.x86_64 already installed and latest version
Nothing to do
# brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.52540098e452 yes virbr0-nic
# ifconfig virbr0
virbr0: flags=4099 mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether 52:54:00:98:e4:52 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# cat /proc/sys/net/ipv4/ip_forward
1

3,准备CentOS 7虚拟机磁盘
# qemu-img create -f qcow2 -o preallocation=metadata /home/vmhome/centos7.qcow2 40G

4,创建CentOS 7虚拟机网卡启动脚本(注意bridge=后的参数,根据实际情况修改)
# chmod a+x /home/vmhome/qemu-ifup
# cat /home/vmhome/qemu-ifup
#!/bin/bash
bridge=virbr0

if [ -n “$1″ ]; then
ip link set $1 up
sleep 1
brctl addif $bridge $1
[ $? -eq 0 ] && exit 0 || exit 1
else
echo “Error: no interfacespecified.”
exit 1
fi

5,开始创建CentOS 7虚拟机
# kvm -name centos7 -smp 4 -m 8192 \
-drive file=/home/vmhome/centos7.qcow2,if=virtio,media=disk,index=0,format=qcow2 \
-drive file=/home/lenky/CentOS-7-x86_64-DVD-1804.iso,media=cdrom,index=1 \
-net nic,model=virtio \
-net tap,ifname=vnet0,script=/home/vmhome/qemu-ifup,downscript=no \
-nographic -vnc :2

启动后,我这里vnc端口应该是5902,可以netstat确认:
# netstat -natp | grep kvm
tcp 0 0 0.0.0.0:5902 0.0.0.0:* LISTEN 14040/kvm
tcp 0 0 199.201.91.208:5902 199.200.152.7:55720 ESTABLISHED 14040/kvm

6,通过tigervnc,连接192.168.1.2:5902,即可看到安装界面,后续操作无需多说。

]]>
http://www.lenky.info/archives/2018/12/2665/feed 0
MongoDB修改监控 http://www.lenky.info/archives/2018/04/2655 http://www.lenky.info/archives/2018/04/2655#comments Fri, 13 Apr 2018 12:36:49 +0000 http://www.lenky.info/?p=2655 我的系统X采用MongoDB存储配置,现在我有一个需求,当某个App1修改了X的配置时,我希望能及时通知其他的App2、App3等。
在MongoDB3.6版本以前,可以利用–replSet副本配置(备注[1]),监控其产生的OpLog来获取修改变化。
在MongoDB3.6版本之后,有了一个名为Change Streams的特性(备注[2]),可以直接满足我这个需求。下面来试一试。

从这里下载MongoDB3.6版本程序:

https://www.mongodb.com/download-center?jmp=homepage#community

我c用的CentOS7.2,因此下载这个:

https://www.mongodb.com/dr/fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.6.5.tgz/download

1,在终端终端Shell-1里先将MongoDB服务运行起来。Change Streams只能在副本集或分片集群中打开,在分片集群中,必须为mongos路由打开Change Streams操作,存储引擎要求是WiredTiger存储引擎。

[root@localhost bin]# pwd
/root/mongodb-linux-x86_64-rhel70-3.6.5/bin
[root@localhost bin]# mkdir  /opt/mongo/
[root@localhost bin]# ./mongod --dbpath /opt/mongo/  --replSet "rs"

另起一个终端Shell-2,执行:
2,创建副本集:

[root@localhost bin]# ./mongo
...
> rs.initiate()
{
	"info2" : "no configuration specified. Using a default configuration for the set",
	"me" : "localhost:27017",
	"ok" : 1,
	"operationTime" : Timestamp(1527211153, 1),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1527211153, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}

3,创建测试数据:

rs:PRIMARY> use demo
switched to db demo
rs:PRIMARY> db.createCollection("coll")
{
	"ok" : 1,
	"operationTime" : Timestamp(1527212586, 1),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1527212586, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}
rs:PRIMARY> db.coll.insert({"test":1})
WriteResult({ "nInserted" : 1 })
rs:PRIMARY> db.coll.find().pretty()
{ "_id" : ObjectId("5b076a4c8d2767c662be613c"), "test" : 1 }

再另起一个终端Shell-3,利用mongo执行如下js:

[root@localhost mongodb-linux-x86_64-rhel70-3.6.5]# cat test.js 
conn = new Mongo("mongodb://localhost:27017/demo?replicaSet=rs");
db = conn.getDB("demo");
collection = db.coll;

const changeStreamCursor = collection.watch();

pollStream(changeStreamCursor);

//this function polls a change stream and prints out each change as it comes in
function pollStream(cursor) {
  while (!cursor.isExhausted()) {
    if (cursor.hasNext()) {
      change = cursor.next();
      print(JSON.stringify(change));
    }
  }
  pollStream(cursor);
}
[root@localhost mongodb-linux-x86_64-rhel70-3.6.5]# ./bin/mongo --quiet test.js 
2018-05-24T22:04:28.533-0400 I NETWORK  [thread1] Starting new replica set monitor for rs/localhost:27017
2018-05-24T22:04:28.681-0400 I NETWORK  [thread1] Successfully connected to localhost:27017 (1 connections now open to localhost:27017 with a 5 second timeout)

回到终端Shell-2的mongo命令行,尝试修改数据:

rs:PRIMARY> db.coll.update({"test":1}, {$set:{"test":2}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

查看终端Shell-3,获得打印信息:

2018-05-24T22:04:28.681-0400 I NETWORK  [thread1] Successfully connected to localhost:27017 (1 connections now open to localhost:27017 with a 5 second timeout)
{"_id":{"_data":{"$binary":"glsHb/QAAAABRmRfaWQAZFsHakyNJ2fGYr5hPABaEATsy7A+TF1ABJv3m91HJyGqBA==","$type":"00"}},"operationType":"update","ns":{"db":"demo","coll":"coll"},"documentKey":{"_id":{"$oid":"5b076a4c8d2767c662be613c"}},"updateDescription":{"updatedFields":{"test":2},"removedFields":[]}}

插入也有类似信息:

rs:PRIMARY> db.coll.insert({"test2":1})
{"_id":{"_data":{"$binary":"glsHcNQAAAABRmRfaWQAZFsHcNSZCR9iEdNTjwBaEATsy7A+TF1ABJv3m91HJyGqBA==","$type":"00"}},"operationType":"insert","fullDocument":{"_id":{"$oid":"5b0770d499091f6211d3538f"},"test2":1},"ns":{"db":"demo","coll":"coll"},"documentKey":{"_id":{"$oid":"5b0770d499091f6211d3538f"}}}

监控还可以设置条件,具体请参考备注[4]。

[1]https://stackoverflow.com/questions/9691316/how-to-listen-for-changes-to-a-mongodb-collection
[2]https://emptysqua.re/blog/driver-features-for-mongodb-3-6/#change-streams
[3]https://dba.stackexchange.com/questions/138300/how-to-get-old-and-new-values-on-redis-notification
[4]https://www.mongodb.com/blog/post/an-introduction-to-change-streams

]]>
http://www.lenky.info/archives/2018/04/2655/feed 0
linux-inject:动态注入替换进程调用函数 http://www.lenky.info/archives/2018/03/2627 http://www.lenky.info/archives/2018/03/2627#comments Sun, 04 Mar 2018 09:20:51 +0000 http://www.lenky.info/?p=2627 最近打算替换Linux下进程的调用函数,这有点类似于LD_PRELOAD环境变量所实现的功能,但是我需要动态的注入,Google了一下,找到几个玩意,linux-injectlinux-injector等。

这里试试linux-inject。

1,系统环境
CentOS 7.2 x86_64

2,下载linux-inject代码

# git clone https://github.com/gaffe23/linux-inject.git
# cd linux-inject
# yum search clang
# make

可能会遇到如下错误,第一个错误安装clang即可,第二个错误不用管(x64平台不用编译32位程序)。

make[1]: clang: Command not found
make[1]: *** [x86_64] Error 127

/usr/include/gnu/stubs.h:7:11: fatal error: 'gnu/stubs-32.h' file not found
# include <gnu/stubs-32.h>

3,运行demo
首先执行./sample-target

# ./sample-target 
sleeping...
sleeping...
...

新开一个终端,执行:./inject -n sample-target sample-library.so

# ./inject -n sample-target sample-library.so
targeting process "sample-target" with pid 27751
"sample-library.so" successfully injected

回到原终端窗口查看:

...
sleeping...
I just got loaded
sleeping...
...

上面是通过-n name指定sample-target进程,也可以通过-p PID的方式来进行指定。
另外,在新版本Linux 3.4中加入的Yama安全模块可能会阻止ptrace-based代码注入,如果测试失败,可以试试关闭这个安全模块:

# echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # 允许普通用户注入代码到自己启动的进程中,root用户可以注入所有进程
# echo 2 | sudo tee /proc/sys/kernel/yama/ptrace_scope # 只允许root用户注入代码

4,代码&原理分析:
sample-library.c源码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/ptrace.h>

/*
 * sleepfunc()
 *
 * The only purpose of this function is to output the message "sleeping..."
 * once a second to provide a more concrete idea of when the sample library
 * gets injected.
 *
 */

void sleepfunc()
{
	struct timespec* sleeptime = malloc(sizeof(struct timespec));

	sleeptime->tv_sec = 1;
	sleeptime->tv_nsec = 0;

	while(1)
	{
		printf("sleeping...\n");
		nanosleep(sleeptime, NULL);
	}

	free(sleeptime);
}

/*
 * main()
 *
 * Call sleepfunc(), which loops forever.
 *
 */

int main()
{
	sleepfunc();
	return 0;
}

代码很简单,就是循环printf和sleep。

sample-library.c源码如下:

#include <stdio.h>
#include <dlfcn.h>

/*
 * hello()
 *
 * Hello world function exported by the sample library.
 *
 */

void hello()
{
	printf("I just got loaded\n");
}

/*
 * loadMsg()
 *
 * This function is automatically called when the sample library is injected
 * into a process. It calls hello() to output a message indicating that the
 * library has been loaded.
 *
 */

__attribute__((constructor))
void loadMsg()
{
	hello();
}

这个代码更简单,但值得注意的是__attribute__((constructor)),这个是gcc为函数提供的类型属性修饰关键字,其中constructor类似于构造函数,即在main函数执行之前或dlopen函数返回之前被调用。

另外一个注入程序inject对应三个源文件utils.c、ptrace.c、inject-x86_64.c,代码就不贴了,其核心思想是利用ptracedlopen向已运行进程中注入.so,执行注入后,该so被link到目标进程的地址空间中,从而目标进程就能“看到”该so中相关符号,比如函数等。同时,利用constructor构造函数能够在so被注入后立即执行的特性,就能进行一些初始化操作。

下面看一个具体的示例:

晕,没调完。。。说明两点:
1,下面的代码还有问题,仅供参考,函数替换肯定能做,但要做得没有问题(不会出现crash)就需要比较细致的功夫,具体可以参考后面的链接。
2,示例中采用__libc_dlopen_mode()是因为这几个函数来之标准C库(这里即glic),不用去链接libdl来使用dlopen(),比较省事。

/*
gcc -Wall -Wextra -Os -o my-sample-target1 my-sample-target1.c
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/ptrace.h>

void hello()
{
	printf("hello\n");
}

int main()
{
    struct timespec* sleeptime = malloc(sizeof(struct timespec));

    sleeptime->tv_sec = 1;
    sleeptime->tv_nsec = 0;

    while(1)
    {
        hello();
        nanosleep(sleeptime, NULL);
    }

    free(sleeptime);

    return 0;
}

/*
gcc -Wall -Wextra -Os -D_GNU_SOURCE -shared -o my-sample-library1.so -fPIC my-sample-library1.c
*/

#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdarg.h>
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>

void* __libc_dlopen_mode(const char*, int);
void* __libc_dlsym(void*, const char*);
int   __libc_dlclose(void*);

void (*hello)();

void world()
{
    printf("world\n");
}

void function_replace(void *target, void *replacement)
{
    assert(((uintptr_t)target & 0x07) == 0); // 8-byte aligned?
    void *page = (void *)((uintptr_t)target & ~0xfff);
    mprotect(page, 4096, PROT_WRITE | PROT_EXEC);
    uint32_t rel = (char *)replacement - (char *)target - 5;
    union {
        uint8_t bytes[8];
        uint64_t value;
    } instruction = { {0xe9, rel >> 0, rel >> 8, rel >> 16, rel >> 24} };
    *(uint64_t *)target = instruction.value;
    mprotect(page, 4096, PROT_EXEC);
}

__attribute__((constructor))
void loadMsg()
{
	void *lib = RTLD_DEFAULT;
    *(void**)(&hello) = __libc_dlsym(lib, "hello");
    if (hello != NULL)
        function_replace(hello, world);
    else
        printf("find function hello failed.\n");
}

/*
gcc -std=c99 -Wall -Wextra -Os -D_GNU_SOURCE -shared -o my-sample-library.so -fPIC my-sample-library.c
*/

#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>

void perror(const char *s);
void* __libc_dlopen_mode(const char*, int);
void* __libc_dlsym(void*, const char*);
int   __libc_dlclose(void*);

int (*old_printf)(const char *format, ...);
int new_printf(const char *format, ...)
{
    va_list ap;

    old_printf("hello world.\n");

    va_start(ap, format);
    old_printf(fformat, ap);
    va_end(ap);
}

void* function_replace(void *target, void *replacement)
{
    assert(((uintptr_t)target & 0x07) == 0); // 8-byte aligned?
    void *page = (void *)((uintptr_t)target & ~0xfff);
    mprotect(page, 4096, PROT_WRITE | PROT_EXEC);
    uint32_t rel = (char *)replacement - (char *)target - 5;
    union {
        uint8_t bytes[8];
        uint64_t value;
    } instruction = { {0xe9, rel >> 0, rel >> 8, rel >> 16, rel >> 24} };
    *(uint64_t *)target = instruction.value;
    mprotect(page, 4096, PROT_EXEC);
}

__attribute__((constructor))
void loadMsg()
{
    void *clib;

    if ((clib = __libc_dlopen_mode("libc.so.6", RTLD_NOW)) == NULL) {
        perror("dlopen libc failed.\n");
        clib = RTLD_DEFAULT;
    }
    
    *(void**)(&old_printf) = __libc_dlsym(clib, "printf");
    
    old_printf = function_replace(old_printf, new_printf)

}

参考链接:

https://www.anquanke.com/post/id/83423

http://blog.csdn.net/hpp24/article/details/52125568

http://www.freebuf.com/articles/system/6388.html

http://packetstormsecurity.nl/mag/phrack/phrack59.tar.gz

http://www.blackhat.com/presentations/bh-europe-01/shaun-clowes/injectso3.ppt

ftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz

http://www.big.net.au/~silvio/lib-redirection.txt

http://online.securityfocus.com/data/library/subversiveld.pdf

https://stackoverflow.com/questions/24355344/inject-shared-library-into-a-process

https://lkubuntu.wordpress.com/2016/01/31/injecting-code-into-running-process-with-linux-inject/

https://www.codeproject.com/Articles/33340/Code-Injection-into-Running-Linux-Application

https://reverseengineering.stackexchange.com/questions/185/how-do-i-add-functionality-to-an-existing-binary-executable

https://www.linuxjournal.com/article/6210

https://stackoverflow.com/questions/3270281/can-gdb-make-a-function-pointer-point-to-another-location

https://stackoverflow.com/questions/9759880/automatically-executed-functions-when-loading-shared-libraries

https://stackoverflow.com/questions/29648919/hook-and-replace-export-function-in-the-loaded-elf-so-shared-library

http://nullprogram.com/blog/2016/03/31/

http://jbremer.org/x86-api-hooking-demystified/

https://blogs.msdn.microsoft.com/oldnewthing/20110921-00/?p=9583

https://lwn.net/Articles/620640/

http://conf.researchr.org/event/pldi-2016/pldi-2016-papers-living-on-the-edge-rapid-toggling-probes-with-cross-modification-on-x86

https://stackoverflow.com/questions/18811446/finding-a-symbol-in-my-own-process

]]>
http://www.lenky.info/archives/2018/03/2627/feed 0
Linux下如何在应用层获取连接跟踪事件 http://www.lenky.info/archives/2018/02/2603 http://www.lenky.info/archives/2018/02/2603#comments Fri, 23 Feb 2018 07:36:21 +0000 http://www.lenky.info/?p=2603 承接上一篇,如果不仅仅只是需要系统当前连接跟踪的静态信息,而需要及时获取连接跟踪的事件信息,又该怎么做?

有两个示例可以获取Linux系统当前的连接跟踪信息,并在连接跟踪事件发生(例如新建连接,断开连接)时进行及时回调:

这里有一个go语言实现的程序:https://github.com/akashihi/conntrack-logger

在源码包libnetfilter_conntrack-1.0.6的utils目录下也有几个类似的实现:
conntrack_events.c、ctexp_events.c、expect_events.c。

以conntrack_events.c为例看看效果:
1,先看看代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <libnetfilter_conntrack/libnetfilter_conntrack.h>

static int event_cb(enum nf_conntrack_msg_type type,
		    struct nf_conntrack *ct,
		    void *data)
{
	static int n = 0;
	char buf[1024];

	nfct_snprintf(buf, sizeof(buf), ct, type, NFCT_O_XML, NFCT_OF_TIME | NFCT_OF_TIMESTAMP);
	printf("%s\n", buf);

	if (++n == 10)
		return NFCT_CB_STOP;

	return NFCT_CB_CONTINUE;
}

int main(void)
{
	int ret;
	struct nfct_handle *h;

	h = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
	if (!h) {
		perror("nfct_open");
		return 0;
	}

	nfct_callback_register(h, NFCT_T_ALL, event_cb, NULL);

	printf("TEST: waiting for 10 events...\n");

	ret = nfct_catch(h);

	printf("TEST: conntrack events ");
	if (ret == -1)
		printf("(%d)(%s)\n", ret, strerror(errno));
	else
		printf("(OK)\n");

	nfct_close(h);

	ret == -1 ? exit(EXIT_FAILURE) : exit(EXIT_SUCCESS);
}

逻辑比较简单,主要是几个函数的使用,具体可以查阅doc文档:http://netfilter.org/projects/libnetfilter_conntrack/
上面的输出是xml格式,为了方便终端里查看,我改为了NFCT_O_PLAIN。

1,首先进行编译:

# gcc conntrack_events.c -o conntrack_events -lnetfilter_conntrack

2,执行程序后,在另外一台机器ping该台机器,即可抓到对应的icmp事件:

# ./conntrack_events 
TEST: waiting for 10 events...
    [NEW] icmp     1 30 src=192.168.137.130 dst=192.168.137.131 type=8 code=0 id=36522 [UNREPLIED] src=192.168.137.131 dst=192.168.137.130 type=0 code=0 id=36522
 [UPDATE] icmp     1 30 src=192.168.137.130 dst=192.168.137.131 type=8 code=0 id=36522 src=192.168.137.131 dst=192.168.137.130 type=0 code=0 id=36522
^C

嗯,貌似很简单,如果没有看到对应的信息,请查看是否加载了nf_conntrack、nf_conntrack_ipv4模块,是否有root权限。

PS:
通过打开对应的内核选项,可以看到更多的信息,例如通过如下命令就可以统计连接上的包数和流量大小:
echo “1” > /proc/sys/net/netfilter/nf_conntrack_acct

nf_conntrack_acct – BOOLEAN 0 – disabled (default)
not 0 – enabled
Enable connection tracking flow accounting. 64-bit byte and packet counters per flow are added.

PS2:
通过加载nf_conntrack_ftp模块,所有ftp关联连接能够注册回调实时捕获。
演示命令:# conntrack -E expect

默认ftp端口就是21。如果修改了ftp服务器端口,那么需要在加载nf_conntrack_ftp模块时指定ftp端口:
# modprobe nf_conntrack_ftp ports=6069

]]>
http://www.lenky.info/archives/2018/02/2603/feed 0
MongoDB的源码编译安装 http://www.lenky.info/archives/2018/01/2620 http://www.lenky.info/archives/2018/01/2620#comments Sun, 28 Jan 2018 15:07:01 +0000 http://www.lenky.info/?p=2620 1,下载源码
从github上下载MongoDB的源码:https://github.com/mongodb/mongo/releases
我这里下载了:https://github.com/mongodb/mongo/archive/r3.6.2.tar.gz
另外,我的OS为CentOS-7.2.x64

2,准备编译环境
解压MongoDB源码包后,在docs/building.md文档里能看到编译当前版本MongoDB所需的编译环境:

* A modern C++ compiler. One of the following is required.
    * GCC 5.4.0 or newer
    * Clang 3.8 (or Apple XCode 8.3.2 Clang) or newer
    * Visual Studio 2015 Update 3 or newer (See Windows section below for details)
* Python 2.7.x and Pip modules:
  * pyyaml
  * typing

i,升级gcc
从gcc的官网下载源码,然后进行解压和编译安装。
# tar -xjf gcc-5.5.0.tar.bz2
# cd gcc-5.5.0
# ./contrib/download_prerequisites
# cd .. && mkdir gcc-build-5.5.0 && cd gcc-build-5.5.0
# ../gcc-5.5.0/configure –prefix=/usr/local/bin/gcc-5.5.0 –enable-checking=release –enable-languages=c,c++ –disable-multilib
# make -j4
# make install
# update-alternatives –install /usr/bin/gcc gcc /usr/local/bin/gcc-5.5.0 66

备注:
遇到错误:

configure: error: error verifying int64_t uses long long
make[2]: *** [configure-stage1-gcc] Error 1
make[2]: Leaving directory `/root/gcc-build-5.5.0'
make[1]: *** [stage1-bubble] Error 2
make[1]: Leaving directory `/root/gcc-build-5.5.0'
make: *** [all] Error 2

解决:

[root@localhost gcc-build-5.5.0]# yum install gcc-c++

ii,升级python
从python的官网下载源码,然后进行解压和编译安装。
# tar xf Python-2.7.14.tar.xz
# cd Python-2.7.14/
# yum install -y bzip2-devel
# ./configure –prefix=/usr/local/python-2.7.14
# make & make install
# update-alternatives –install /usr/bin/python python /usr/local/bin/python-2.7.14 69

iii,安装scons
官网上找到源码包直接安装。
# yum install pcre-devel python-devel
# tar -zxvf scons-3.0.1.tar.gz && cd scons-3.0.1
# python setup.py install

iv,安装Python模块
# pip install –upgrade pip
# pip2 install -r buildscripts/requirements.txt

3,编译MongoDB
我这编译遇到这个错误:
[root@localhost mongo-r3.6.2]# python2 buildscripts/scons.py all -j 4
scons: Reading SConscript files …
Invalid MONGO_VERSION ”, or could not derive from version.json or git metadata. Please add a conforming MONGO_VERSION=x.y.z[-extra] as an argument to SCons

需要手动指定版本号:
# python2 buildscripts/scons.py all MONGO_VERSION=3.6.2 -j 4

all为全部编译,这包括mongod, mongo, tests, 等等,需要较长时间,也需要比较多的磁盘空间,我这里只编译core(包括:mongod, mongos, mongo)就行了。
# python2 buildscripts/scons.py core MONGO_VERSION=3.6.2 -j 4

4,安装
# python2 buildscripts/scons.py –prefix=/opt/mongo install MONGO_VERSION=3.6.2
# cd /opt/mongo/bin/
# ls
install_compass mongo mongod mongoperf mongos

5,运行
# cd /opt/mongo/
# mkdir -p /opt/data/mongo
# bin/mongod
bin/mongod: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21′ not found (required by bin/mongod)
bin/mongod: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20′ not found (required by bin/mongod)
bin/mongod: /lib64/libstdc++.so.6: version `CXXABI_1.3.8′ not found (required by bin/mongod)
# export LD_LIBRARY_PATH=/usr/local/bin/gcc-5.5.0/lib64
# bin/mongod –dbpath /opt/data/mongo

6,验证
新开一个终端,进行连接:
# export LD_LIBRARY_PATH=/usr/local/bin/gcc-5.5.0/lib64
# bin/mongo

Over~

参考:
http://www.runoob.com/mongodb/mongodb-tutorial.html

]]>
http://www.lenky.info/archives/2018/01/2620/feed 0
Linux下如何在应用层获取连接跟踪信息 http://www.lenky.info/archives/2018/01/2599 http://www.lenky.info/archives/2018/01/2599#comments Sun, 28 Jan 2018 07:35:13 +0000 http://www.lenky.info/?p=2599 一,办法1
写个内核模块,吧啦吧啦,这个看上去比较容易,但缺陷是:
1,如果要支持的系统环境比较复杂,比如有Ubuntu、CentOS、Fedora等不同的发行版,各个发行版还有不同的版本如Ubuntu12.04、Ubuntu14.04等,那么维护的工作量非常巨大。
2,内核代码一出错就宕机,风险极大。

二,办法2
采用pcap将数据包抓到应用层,分析数据包来进行连接跟踪,缺陷是包分析的工作量极大,抓包的对系统性能的极大损耗,而且万一有漏包,会导致连接跟踪信息不准确。
因此比较好的方式是直接利用系统自带的接口来获取,而Linux系统的nf_conntrack模块就提供了这个接口:

[root@localhost ~]# cat /proc/net/nf_conntrack
ipv4     2 tcp      6 431945 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=23474 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=23474 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
ipv4     2 tcp      6 299 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=21744 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=21744 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
ipv4     2 tcp      6 429995 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=35167 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=35167 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2

上面接口是在CentOS 7.2里看到的,在比较老的CentOS系统上,可能是/proc/net/ip_conntrack,而在Ubuntu上,可能这两个接口都没有,原因是这个接口被摒弃,需要采用conntrack命令来查看:

[root@localhost ~]# conntrack -L
tcp      6 431970 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=23474 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=23474 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
tcp      6 299 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=21744 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=21744 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
tcp      6 431224 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=35167 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=35167 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
conntrack v1.4.4 (conntrack-tools): 3 flow entries have been shown.

当然,conntrack命令需要先采用yum install conntrack-tools或sudo apt-get install conntrack进行安装。

如果程序要想获取原始的连接跟踪信息,怎么办呢?非常好办,直接看看conntrack的源码即可。我通过apt-get source conntrack获取到conntrack的源码,然后把里面的一部分代码挖出来如下:

#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <signal.h>
#include <string.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>

static struct nfct_handle *cth, *ith;
struct u32_mask {
	uint32_t value;
	uint32_t mask;
};

/* These are the template objects that are used to send commands. */
static struct {
	struct nf_conntrack *ct;
	struct nf_expect *exp;
	/* Expectations require the expectation tuple and the mask. */
	struct nf_conntrack *exptuple, *mask;

	/* Allows filtering/setting specific bits in the ctmark */
	struct u32_mask mark;

	/* Allow to filter by mark from kernel-space. */
	struct nfct_filter_dump_mark filter_mark_kernel;
} tmpl;

static int counter;

static unsigned int options;

static unsigned int output_mask;

enum ct_options {
	CT_OPT_ORIG_SRC_BIT	= 0,
	CT_OPT_ORIG_SRC 	= (1 << CT_OPT_ORIG_SRC_BIT),

	CT_OPT_ORIG_DST_BIT	= 1,
	CT_OPT_ORIG_DST		= (1 << CT_OPT_ORIG_DST_BIT),

	CT_OPT_ORIG		= (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST),

	CT_OPT_REPL_SRC_BIT	= 2,
	CT_OPT_REPL_SRC		= (1 << CT_OPT_REPL_SRC_BIT),

	CT_OPT_REPL_DST_BIT	= 3,
	CT_OPT_REPL_DST		= (1 << CT_OPT_REPL_DST_BIT),

	CT_OPT_REPL		= (CT_OPT_REPL_SRC | CT_OPT_REPL_DST),

	CT_OPT_PROTO_BIT	= 4,
	CT_OPT_PROTO		= (1 << CT_OPT_PROTO_BIT),

	CT_OPT_TUPLE_ORIG	= (CT_OPT_ORIG | CT_OPT_PROTO),
	CT_OPT_TUPLE_REPL	= (CT_OPT_REPL | CT_OPT_PROTO),

	CT_OPT_TIMEOUT_BIT	= 5,
	CT_OPT_TIMEOUT		= (1 << CT_OPT_TIMEOUT_BIT),

	CT_OPT_STATUS_BIT	= 6,
	CT_OPT_STATUS		= (1 << CT_OPT_STATUS_BIT),

	CT_OPT_ZERO_BIT		= 7,
	CT_OPT_ZERO		= (1 << CT_OPT_ZERO_BIT),

	CT_OPT_EVENT_MASK_BIT	= 8,
	CT_OPT_EVENT_MASK	= (1 << CT_OPT_EVENT_MASK_BIT),

	CT_OPT_EXP_SRC_BIT	= 9,
	CT_OPT_EXP_SRC		= (1 << CT_OPT_EXP_SRC_BIT),

	CT_OPT_EXP_DST_BIT	= 10,
	CT_OPT_EXP_DST		= (1 << CT_OPT_EXP_DST_BIT),

	CT_OPT_MASK_SRC_BIT	= 11,
	CT_OPT_MASK_SRC		= (1 << CT_OPT_MASK_SRC_BIT),

	CT_OPT_MASK_DST_BIT	= 12,
	CT_OPT_MASK_DST		= (1 << CT_OPT_MASK_DST_BIT),

	CT_OPT_NATRANGE_BIT	= 13,
	CT_OPT_NATRANGE		= (1 << CT_OPT_NATRANGE_BIT),

	CT_OPT_MARK_BIT		= 14,
	CT_OPT_MARK		= (1 << CT_OPT_MARK_BIT),

	CT_OPT_ID_BIT		= 15,
	CT_OPT_ID		= (1 << CT_OPT_ID_BIT),

	CT_OPT_FAMILY_BIT	= 16,
	CT_OPT_FAMILY		= (1 << CT_OPT_FAMILY_BIT),

	CT_OPT_SRC_NAT_BIT	= 17,
	CT_OPT_SRC_NAT		= (1 << CT_OPT_SRC_NAT_BIT),

	CT_OPT_DST_NAT_BIT	= 18,
	CT_OPT_DST_NAT		= (1 << CT_OPT_DST_NAT_BIT),

	CT_OPT_OUTPUT_BIT	= 19,
	CT_OPT_OUTPUT		= (1 << CT_OPT_OUTPUT_BIT),

	CT_OPT_SECMARK_BIT	= 20,
	CT_OPT_SECMARK		= (1 << CT_OPT_SECMARK_BIT),

	CT_OPT_BUFFERSIZE_BIT	= 21,
	CT_OPT_BUFFERSIZE	= (1 << CT_OPT_BUFFERSIZE_BIT),

	CT_OPT_ANY_NAT_BIT	= 22,
	CT_OPT_ANY_NAT		= (1 << CT_OPT_ANY_NAT_BIT),

	CT_OPT_ZONE_BIT		= 23,
	CT_OPT_ZONE		= (1 << CT_OPT_ZONE_BIT),
};

#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \
		       CT_OPT_MARK | CT_OPT_SECMARK |  CT_OPT_STATUS | \
		       CT_OPT_ID | CT_OPT_ZONE)

enum {
	_O_XML	= (1 << 0),
	_O_EXT	= (1 << 1),
	_O_TMS	= (1 << 2),
	_O_ID	= (1 << 3),
	_O_KTMS	= (1 << 4),
};

static int dump_xml_header_done = 1;

static int mark_cmp(const struct u32_mask *m, const struct nf_conntrack *ct)
{
	return nfct_attr_is_set(ct, ATTR_MARK) &&
		(nfct_get_attr_u32(ct, ATTR_MARK) & m->mask) == m->value;
}

static int
filter_mark(const struct nf_conntrack *ct)
{
	if ((options & CT_OPT_MARK) &&
	     !mark_cmp(&tmpl.mark, ct))
		return 1;
	return 0;
}


static int 
filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct)
{
	int check_srcnat = options & CT_OPT_SRC_NAT ? 1 : 0;
	int check_dstnat = options & CT_OPT_DST_NAT ? 1 : 0;
	int has_srcnat = 0, has_dstnat = 0;
	uint32_t ip;
	uint16_t port;

	if (options & CT_OPT_ANY_NAT)
		check_srcnat = check_dstnat = 1;

	if (check_srcnat) {
		int check_address = 0, check_port = 0;

		if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4)) {
			check_address = 1;
			ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4);
			if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) &&
			    ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST))
				has_srcnat = 1;
		}
		if (nfct_attr_is_set(obj, ATTR_SNAT_PORT)) {
			int ret = 0;

			check_port = 1;
			port = nfct_get_attr_u16(obj, ATTR_SNAT_PORT);
			if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT) &&
			    port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST))
				ret = 1;

			/* the address matches but the port does not. */
			if (check_address && has_srcnat && !ret)
				has_srcnat = 0;
			if (!check_address && ret)
				has_srcnat = 1;
		}
		if (!check_address && !check_port &&
		    (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ||
		     nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT)))
		  	has_srcnat = 1;
	}
	if (check_dstnat) {
		int check_address = 0, check_port = 0;

		if (nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) {
			check_address = 1;
			ip = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4);
			if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) &&
			    ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC))
				has_dstnat = 1;
		}
		if (nfct_attr_is_set(obj, ATTR_DNAT_PORT)) {
			int ret = 0;

			check_port = 1;
			port = nfct_get_attr_u16(obj, ATTR_DNAT_PORT);
			if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT) &&
			    port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC))
				ret = 1;

			/* the address matches but the port does not. */
			if (check_address && has_dstnat && !ret)
				has_dstnat = 0;
			if (!check_address && ret)
				has_dstnat = 1;
		}
		if (!check_address && !check_port &&
		    (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ||
		     nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT)))
			has_dstnat = 1;
	}
	if (options & CT_OPT_ANY_NAT)
		return !(has_srcnat || has_dstnat);
	else if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT))
		return !(has_srcnat && has_dstnat);
	else if (options & CT_OPT_SRC_NAT)
		return !has_srcnat;
	else if (options & CT_OPT_DST_NAT)
		return !has_dstnat;

	return 0;
}


static int dump_cb(enum nf_conntrack_msg_type type,
		   struct nf_conntrack *ct,
		   void *data)
{
	char buf[1024];
	struct nf_conntrack *obj = data;
	unsigned int op_type = NFCT_O_DEFAULT;
	unsigned int op_flags = 0;

	if (filter_nat(obj, ct))
		return NFCT_CB_CONTINUE;

	if (filter_mark(ct))
		return NFCT_CB_CONTINUE;

	if (options & CT_COMPARISON &&
	    !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
		return NFCT_CB_CONTINUE;

	if (output_mask & _O_XML) {
		op_type = NFCT_O_XML;
		if (dump_xml_header_done) {
			dump_xml_header_done = 0;
			printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
			       "<conntrack>\n");
		}
	}
	if (output_mask & _O_EXT)
		op_flags = NFCT_OF_SHOW_LAYER3;
	if (output_mask & _O_KTMS)
		op_flags |= NFCT_OF_TIMESTAMP;
	if (output_mask & _O_ID)
		op_flags |= NFCT_OF_ID;

	nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
	printf("%s\n", buf);

	counter++;

	return NFCT_CB_CONTINUE;
}


int main()
{
	cth = nfct_open(CONNTRACK, 0);
		if (!cth)
			perror("Can't open handler");



	
nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);


	struct nfct_filter_dump *filter_dump;

	int res;


int family = AF_UNSPEC;
	if (family == AF_UNSPEC)
		family = AF_INET;


		filter_dump = nfct_filter_dump_create();
		if (filter_dump == NULL)
			perror("OOM");

		nfct_filter_dump_set_attr(filter_dump, NFCT_FILTER_DUMP_MARK,
					  &tmpl.filter_mark_kernel);
		nfct_filter_dump_set_attr_u8(filter_dump,
					     NFCT_FILTER_DUMP_L3NUM,
					     family);

		if (options & CT_OPT_ZERO)
			res = nfct_query(cth, NFCT_Q_DUMP_FILTER_RESET,
					filter_dump);
		else
			res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump);

		nfct_filter_dump_destroy(filter_dump);

		if (dump_xml_header_done == 0) {
			printf("</conntrack>\n");
			fflush(stdout);
		}

		nfct_close(cth);

	return 0;
}

上面这段代码完全来之conntrack源码,我只修改了极少部分,以便让它能够正确通过编译。编译这段代码需要先安装两个库,然后再用gcc进行编译:

# yum search libmnl
# yum install libmnl-devel
# yum search libnetfilter
# yum install libnetfilter_conntrack-devel
# gcc test2.c -lmnl -lnetfilter_conntrack

执行生成的a.out就能看到对应的连接跟踪信息:

[root@localhost lenky]# ./a.out 
tcp      6 431967 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=23474 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=23474 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
tcp      6 299 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=21744 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=21744 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
tcp      6 429541 ESTABLISHED src=192.168.137.1 dst=192.168.137.131 sport=35167 dport=22 src=192.168.137.131 dst=192.168.137.1 sport=22 dport=35167 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1

如果没有正常输出,需要排查一下nf_conntrack、nf_conntrack_ipv4内核模块是否没有加载,另外需要以root权限执行程序才能获取到对应的信息。

]]>
http://www.lenky.info/archives/2018/01/2599/feed 0
一篇好文章:《为什么好产品会失败》 http://www.lenky.info/archives/2016/12/2564 http://www.lenky.info/archives/2016/12/2564#comments Sat, 17 Dec 2016 13:52:09 +0000 http://www.lenky.info/?p=2564 很多创新型新产品没有在市场上取得成功,一个最常见的原因是公司没有充分理解客户是如何对产品进行评估和做购买决定的。

这是一篇问答式文章,问题很明确:为什么创新型新产品没有在市场上取得成功?

那答案呢?下面是我的总结,只是总结,不是翻译,看看就好,详情见末尾原文。

我的产品很牛逼,但是客户没有选择我,为什么?有两种可能情况:

1,你说你好你牛逼,但我不知道啊!
举例:英国Aqualisa公司专门针对英国住户的低水压问题研发了一种新式的淋浴系统,但是英国住户已经习惯了英国了低水压问题,并没有想到要去寻找更换一件创新性系统。
—> 如果我知道你这么牛逼,我会选择购买你。
—> 属于需要被教育的用户。

2,你说你好你牛逼,但我找不到你啊!
举例:2014年,纽约市的计程车车祸数量比计程车数量还多,因此如果能找到一位安全靠谱的的士司机是相当重要的,但是当我携带大包小包从机场楼出来的时候,我只想快速找个的士就走,哪还有空去挑安全靠谱的的士司机。
—> 如果我能轻易的知道牛逼的就是你,我会选择购买你。
—> 选择成本太高,超过用户可承受能力

上面是两种特例,更多的情况是处于中间状况,即用户会进行选择,而选择的过程就是不断的进行信息获取并进行判断,直至最终促使用户做出最终的购买决定。这个过程的时间长短会因人而异并且受较多因素影响,但一般情况下,与购买目标的价值成正比,例如购买新车的选择时间就会比购买餐具洗涤剂的选择时间要长。当然,不排除有土豪,直接买买买~。

信息的获取主要有两种途径:搜索和推测,其中推测就是用可搜索信息来猜测无法轻易搜索得到的信息。

用户的搜索程度和其专业知识之间的关系如下图所示:
simester-s1

为什么是这样的图形,解释是这样的:牛逼的人不用搜,因为我啥都知道。小小白也不用搜,因为我不知道怎么搜。
这个在专业性非常强的行业可能会比较明显,例如最近研发了一种新药,效果好,副作用少,但医生却并不给病人开这个药,为撒?因为医生认为对于治疗这种疾病,他们了解得足够多了,用旧药就行了,没必要使用新药。
文章中还有其他例子,例如小白去买自行车,啥也不懂,还不是听店员一吹牛逼,然后颜色外观好看、价格合适也就买了,至于创新点在她面前并没有受到更多的关注。

那么如何影响用户的购买决策呢?公司只有充分理解客户是如何对产品进行评估和做购买决定的,让产品创新点出现在客户做购买决定的关键路径上,才能赢得市场。

这里有几个点值得注意:

1,推测和无意识推测
除去不做搜索的老司机和小小白,大多数用户都会进行信息搜索和推测。例如,通过麦当劳停车区域的干净与否来推测其厨房或卫生间是否干净,从而决定是否要在此麦当劳购买就餐。
搜索和推测是同步进行的,并且相互影响,一些推测是无意识的,或容易被五官所欺骗而潜意识进行的。例如下图中,棋盘上A格和B格看上去颜色不一样,但事实上,它们就是一样的。
Simester-s2

2,品牌价值
用户搜索越有效,对品牌的依赖就越小。当用户没法进行有效的搜索时,用户就会采用价格和品牌来做质量推断。通常所说的,买东西要买大品牌,亦或,只买贵的,不买对的。因此,品牌价值对新用户更大,因为新用户对目标产品了解甚少,能进行的有效搜索也相对较少。一旦新用户变成老用户,那么品牌价值就变小了,除非目标产品真的很不错,让用户对品牌非常认可。

3,网络影响
网络的影响主要是用户对信息的搜索获取变得更加容易,特别是智能手机的出现,使得用户能够随时随地的进行搜索。例如,附近哪家饭店好吃?即查即用。
网络带来的另外一个影响是加速了产品正面或负面新闻的传播,加剧竞争。

总结:
对于从事创新型产品开发的公司来说,其目标是明确的:
不仅要创造能够给用户带来实际价值的产品,更应该让用户能够关注到这些价值。

具体怎么做?关注用户能否发现产品的创新点的三个方面:

a)调查的动力
客户是否觉得这种创新是重要的?
客户是否觉得已经拥有这种功能?
创新产品和现有产品有什么差别?
典型客户的购买决策过程是多久?

b)调查的能力
展开搜索的难易程度?
产品改进点标明在供需清单上还是其他类似地方?
客户能否理解相关信息?
客户是否需要改变搜索流程?

c)客户的推断
客户使用哪些线索进行推断?
客户会从线索中得出哪些推断?
可以控制或影响这些线索吗?

最后,一言以蔽之,公司要把重点放在用户容易关注到的创新点,而对于用户自身无法发现的创新点,找些方法给用户以提示。

原文:
http://sloanreview.mit.edu/article/why-great-new-products-fail/

]]>
http://www.lenky.info/archives/2016/12/2564/feed 0
VPP安装 http://www.lenky.info/archives/2016/11/2562 http://www.lenky.info/archives/2016/11/2562#comments Sat, 19 Nov 2016 01:18:48 +0000 http://www.lenky.info/?p=2562 Cisco VPP,全称Vector Packet Processing,是Cisco 2002年开发的商用代码。在2016年2月11号,Linux基金会创建了FD.io项目,Cisco就将其VPP代码的开源版本贡献到了该项目,而目前已成为该项目的核心工程。VPP运行于用户空间,支持多种收包方式,常用的是DPDK。VPP的特色主要有两个,一是框架可扩展,二是具备成熟的交换/路由功能。

文字描述少说,首先准备环境,看VPP如何安装试用。

参考官方文档:
https://wiki.fd.io/view/VPP/Pulling,_Building,_Running,_Hacking_and_Pushing_VPP_Code#Building_the_first_time
https://wiki.fd.io/view/VPP/Build,_install,_and_test_images#Build_A_VPP_Package
https://wiki.fd.io/view/VPP/Setting_Up_Your_Dev_Environment

1,系统环境:
操作系统是CentOS-7.2 x86_64,安装在VMware? Workstation 12 Pro上。

2,下载源码:
git clone –depth=1 https://gerrit.fd.io/r/vpp
上面命令有坑(我刚开始为了节省下载时间,用的上面命令,但是在后面编译时报错),请使用:
git clone https://gerrit.fd.io/r/vpp
进行下载。

3,安装依赖(貌似是非必要步骤):
[root@localhost vpp]# make install-dep

4,第一次安装,直接执行./build-root/vagrant/build.sh脚本即可:

[root@localhost vpp]# pwd
/root/vpp
[root@localhost vpp]# ./build-root/vagrant/build.sh 

等待了很长一段时间,全程无错。

如果后续需要进行源码更新和编译,则参考下面两步:
https://wiki.fd.io/view/VPP/Build,_install,_and_test_images#Build_A_VPP_Package

5,生成Makefile文件:
[root@localhost vpp]# git pull
[root@localhost vpp]# cd build-root/
[root@localhost build-root]# make distclean; ./bootstrap.sh

6,进行编译:
[root@localhost build-root]# make V=0 PLATFORM=vpp TAG=vpp install-rpm;
如果是Debian系,例如Ubuntu,则执行:
[root@localhost build-root]# make V=0 PLATFORM=vpp TAG=vpp install-deb;

这其中会下载dpdk-16.07.tar.xz(当前是这个版本),如果下载很慢,直接用迅雷下吧,下载好了直接放在/root/vpp/dpdk/目录即可(可以通过ps查看是在用curl进行下载,通过curl的参数可以看到下载源地址和目的路径)。

可能遇到的错误(我在摸索的过程中遇到的错误):

make: *** /lib/modules/3.10.0-327.el7.x86_64/build: No such file or directory.  Stop.
make[10]: *** [igb_uio.ko] Error 2
make[9]: *** [igb_uio] Error 2
make[9]: *** Waiting for unfinished jobs....
...
make[8]: *** [linuxapp] Error 2
make[7]: *** [librte_eal] Error 2
make[6]: *** [lib] Error 2
make[5]: *** [all] Error 2
make[4]: *** [pre_install] Error 2
make[3]: *** [install] Error 2
make[3]: Leaving directory `/root/vpp/build-root/build-vpp-native/dpdk/dpdk-16.07'
make[2]: *** [/root/vpp/build-root/build-vpp-native/dpdk/.build.ok] Error 2
make[2]: Leaving directory `/root/vpp/dpdk'
make[1]: *** [dpdk-install] Error 2
make[1]: Leaving directory `/root/vpp/build-root'
make: *** [install-rpm] Error 1

解决:
a) 首先安装内核头文件:
[root@localhost build-root]# sudo yum install kernel-headers
b) 然后查看一下找不到的build文件:
[root@localhost build-root]# ls -l /lib/modules/3.10.0-327.el7.x86_64/build
lrwxrwxrwx. 1 root root 38 May 28 09:13 /lib/modules/3.10.0-327.el7.x86_64/build -> /usr/src/kernels/3.10.0-327.el7.x86_64
c) 发现/usr/src/kernels/3.10.0-327.el7.x86_64扔不存在,但已存在/usr/src/kernels/3.10.0-327.36.3.el7.x86_64
根据uname来看:
[root@localhost build-root]# uname -a
Linux localhost.localdomain 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
猜测编译igb用的是uname获取的内核版本号,但小版本好36没有出现在uname里,因此直接这样处理一下:
[root@localhost build-root]# ln -s /usr/src/kernels/3.10.0-327.36.3.el7.x86_64 /usr/src/kernels/3.10.0-327.el7.x86_64

可能遇到的遇到错误(我在摸索的过程中遇到的错误):

make[1]: Leaving directory `/root/vpp/build-root'
fatal: No names found, cannot describe anything.
fatal: No names found, cannot describe anything.
error: line 26: Empty tag: Version:
make: *** [install-rpm] Error 1

上面的信息貌似是git报出来的,因此采用git clone https://gerrit.fd.io/r/vpp进行源码完整下载,后重试。

7,包安装:

[root@localhost build-root]# ls *.rpm
vpp-17.01-rc0~64_gcfba1e2.x86_64.rpm            vpp-devel-17.01-rc0~64_gcfba1e2.x86_64.rpm  vpp-plugins-17.01-rc0~64_gcfba1e2.x86_64.rpm
vpp-debuginfo-17.01-rc0~64_gcfba1e2.x86_64.rpm  vpp-lib-17.01-rc0~64_gcfba1e2.x86_64.rpm    vpp-python-api-17.01-rc0~64_gcfba1e2.x86_64.rpm
[root@localhost build-root]# rpm -ivh *.rpm

8,安装好后,会自动安装默认配置:

[root@localhost build-root]# cat /etc/vpp/startup.conf 

unix {
  nodaemon
  log /tmp/vpp.log
  full-coredump
}

dpdk {
    uio-driver uio_pci_generic
}

api-trace {
  on
}

api-segment {
  gid vpp
}

9,启动vpp:

[root@localhost build-root]# systemctl start vpp
[root@localhost build-root]# ps aux | grep vpp
root      75046  4.5  0.9 2477080 26056 ?       Ssl  04:59   0:01 /usr/bin/vpp -c /etc/vpp/startup.conf
root      75069  0.0  0.0 112648   976 pts/1    S+   04:59   0:00 grep --color=auto vpp

10,试试:

[root@localhost build-root]# vppctl show interface 
              Name               Idx       State          Counter          Count     
local0                            0        down      

over~

]]>
http://www.lenky.info/archives/2016/11/2562/feed 0
伪装蜜罐 http://www.lenky.info/archives/2016/11/2549 http://www.lenky.info/archives/2016/11/2549#comments Sun, 13 Nov 2016 03:44:45 +0000 http://www.lenky.info/?p=2549 关于伪装技术,今天又继续看了几篇文章,以这篇文章:
http://www.itworldcanada.com/article/deception-technology-enhances-honeypots-for-cyber-defence/380544
为起点,又搜寻出不少相关信息。

1,国外这方面的公司更多:
Attivo: https://attivonetworks.com/
Allure Security Technology: https://www.alluresecurity.com/
CyberTrap: http://www.cybertrap.com/
Cymmetria: http://www.cymmetria.com/
ForeScout: https://www.forescout.com/
GuardiCore: http://www.guardicore.com/
Hexis Cyber Solutions: https://www.hexiscyber.com/
Illusive Networks:
LogRhythm: https://logrhythm.com/
Percipient Networks: https://strongarm.io/
Rapid7:
Shape Security:
Specter:
TrapX Security: http://trapx.com/
TopSpin Security:

看来专注于这个领域的公司还真多。

2,专业的伪装产品更复杂,模拟的环境更真实,例如会包括终端、网络、应用程序、数据等各种相关层次的模拟伪装。
蜜罐是伪装技术的典型应用,采用蜜罐进行安全防御和情报收集可以追溯到15年以前。有商业蜜罐,也有更多的基于开源的蜜罐:
Snort: https://www.snort.org/
Dionaea: https://github.com/rep/dionaea
Conpot: http://conpot.org/
Shiva:
Nepenthes:
The Honeynet Project: https://www.honeynet.org/,非营利安全研究机构,致力于研究最新的攻击和开发开源安全工具来提高网络安全。
Modern Honey Network (MHN): http://threatstream.github.io/mhn/

开源蜜罐的缺点在于难以管理和规模部署,而在这方面,商业产品能做得更好,例如与SIEM安全信息和事件管理系统进行集成。
但开源领域也做出了不少努力,例如上面提到的MHN。MHN的主要目标是简化蜜罐部署,同时进行数据的收集和整理。我参考这里:http://threatstream.github.io/mhn/进行了实际部署,一起看看。

3,系统环境【注意:这里有坑,请跳到第7步】

lenky@lenky-virtual-machine:~$ cat /etc/issue
Ubuntu 16.04.1 LTS n l

lenky@lenky-virtual-machine:~$ uname -a
Linux lenky-virtual-machine 4.4.0-31-generic #50-Ubuntu SMP Wed Jul 13 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

4,下载源码包

lenky@lenky-virtual-machine:~$ wget https://github.com/threatstream/mhn/zipball/master
lenky@lenky-virtual-machine:~$ unzip master
lenky@lenky-virtual-machine:~$ cd threatstream-mhn-0474d0d/

5,安装

lenky@lenky-virtual-machine:~/threatstream-mhn-0474d0d$ sudo ./install.sh 

出错:

下列软件包有未满足的依赖关系:
 python-dev : 依赖: python2.7-dev (>= 2.7.11-1~) 但是它将不会被安装
 python-pip : 依赖: python-pip-whl (= 8.1.1-2) 但是 8.1.1-2ubuntu0.2 正要被安装
              推荐: python-all-dev (>= 2.6) 但是它将不会被安装
              推荐: python-setuptools 但是它将不会被安装
              推荐: python-wheel 但是它将不会被安装
E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。

解决:

lenky@lenky-virtual-machine:~/threatstream-mhn-0474d0d$ sudo apt-get install aptitude
lenky@lenky-virtual-machine:~/threatstream-mhn-0474d0d$ sudo aptitude install python-dev python-pip

要先选择n,不接受保持解决方案,再选择y,接受降级解决方案。
再执行sudo ./install.sh进行安装,等待,可能还会遇到其他错误,例如pip下载超时等,我遇到问题的解决办法总结:
a) sudo时加上-H,便于pip可以利用缓存,否则提示:

The directory '/home/lenky/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/lenky/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.

如果要反复执行./install.sh进行重复安装,上面这个能够避免重复下载。

b) 看懂安装脚本,避免重复执行某些动作,例如我这里在进行install_hpfeeds.sh时反复几次出错,每次重新执行./install.sh都会重新安装下载libev和hpfeeds,所以直接把里面的相关语句进行注释掉,避免多余动作,加快重试速度。

c) 出错:

+ pip install -e git+https://github.com/rep/evnet.git#egg=evnet-dev
Obtaining evnet from git+https://github.com/rep/evnet.git#egg=evnet-dev
  Updating ./env/src/evnet clone
Collecting pyev>=0.5.3-3.8 (from evnet)
  Using cached pyev-0.9.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-SSAIqn/pyev/setup.py", line 56, in <module>
        check_version(python_version, min_python_versions[major], "Python{0}".format(major))
      File "/tmp/pip-build-SSAIqn/pyev/setup.py", line 40, in check_version
        if StrictVersion(current_version) < StrictVersion(minimum_version):
      File "/usr/lib/python2.7/distutils/version.py", line 40, in __init__
        self.parse(vstring)
      File "/usr/lib/python2.7/distutils/version.py", line 107, in parse
        raise ValueError, "invalid version number '%s'" % vstring
    ValueError: invalid version number '2.7.11+'
    
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-SSAIqn/pyev/

官方当前未解决的问题:https://github.com/rep/evnet/issues/8
想办法解决上面的问题,但继续进行安装时又有其他问题,各种坑,算了,根据官方issue来看:https://github.com/threatstream/mhn/issues/286
目前mhn没法很好的支持Ubuntu 16.04。

7,利用ubuntu-14.04.1-server-amd64.iso安装了一个Ubuntu 14.04的新系统:

lenky@ubuntu:~$ uname -a
Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
lenky@ubuntu:~$ cat /etc/issue
Ubuntu 14.04.1 LTS \n \l

新安装好后,把threatstream-mhn-0474d0d.zip拷贝上来,解压后执行:

lenky@ubuntu:~/threatstream-mhn-0474d0d$ sudo -H ./install.sh 

竟然一步到底,无任何错误,最后需要进行几步交互式设置,请记住设置的超级管理员的邮箱和密码:

Superuser email: lenky0401@163.com
Superuser password: 
Superuser password: (again): 

安装OK,果然在一开始就要“选对人”很重要。
注:如果pip安装总是提示超时,建议先设置pip的超时时限和设置下载镜像站点(请自行检查一下镜像站点是否可用):http://blog.csdn.net/dszgf5717/article/details/53138298

建个文件 ~/.pip/pip.conf, 内容如下:
[global]
timeout = 6000
index-url = http://pypi.douban.com/simple/
[install]
use-mirrors = true
mirrors = http://pypi.douban.com/simple/
trusted-host = pypi.douban.com

8,登录web:
nhm安装好后,会通过nginx监听80端口,所以用浏览器打开对应的地址(注意更改IP):

http://192.168.19.130/

然后输入前面设置的邮箱和密码即可进行nhm进行信息查看,当然刚开始是没有数据的。

参考:
http://www.govtech.com/blogs/lohrmann-on-cybersecurity/Free-Open-Source-Security-Tools-Offer-Intelligence-Based-Defense.html
http://www.americanbanker.com/news/bank-technology/deception-may-be-the-best-way-to-catch-cybercriminals-1076667-1.html
https://www.threatstream.com/blog/mhn-modern-honey-network
http://www.freebuf.com/articles/network/111155.html
http://www.freebuf.com/articles/database/109322.html
http://www.2cto.com/article/201505/397719.html

以关键字”Deception technology opensource”进行Google搜索

]]>
http://www.lenky.info/archives/2016/11/2549/feed 0