溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

系統(tǒng)panic后主動(dòng)廣播最后內(nèi)核dmesg信息-一個(gè)幾乎可以使用的方案

發(fā)布時(shí)間:2020-06-10 20:39:52 來源:網(wǎng)絡(luò) 閱讀:1319 作者:dog250 欄目:網(wǎng)絡(luò)安全

在《遠(yuǎn)程觸發(fā)SYSRQ獲取最新的dmesg信息-一個(gè)幾乎沒有什么用的方案》中,我認(rèn)為遠(yuǎn)程觸發(fā)SYSRQ并沒有什么實(shí)際的用處,系統(tǒng)沒有掛起時(shí),用SSH等標(biāo)準(zhǔn)方式會(huì)好很多,系統(tǒng)掛起時(shí),遠(yuǎn)程觸發(fā)在多數(shù)情況下均無法得到響應(yīng)。那么有什么方法在系統(tǒng)panic的時(shí)候通知外部呢?
       當(dāng)然采用crash kexec kernel的方式會(huì)是一個(gè)好方法,但是那畢竟是為了debug而生的,有的時(shí)候只是想知道dmesg的最后幾行就好,要知道此時(shí)系統(tǒng)已經(jīng)完全沒有機(jī)會(huì)記 錄日志了,用戶態(tài)的任何進(jìn)程均已經(jīng)全軍覆沒。但是別忘了還有一個(gè)口子可以將最后的信息送出去,那就是網(wǎng)絡(luò)。為了使得信息更加可能被接收,我覺得采用廣播會(huì) 比較好。具體的方式我在《遠(yuǎn)程觸發(fā)SYSRQ獲取最新的dmesg信息-一個(gè)幾乎沒有什么用的方案》的最后已經(jīng)給出了。本文主要給出一個(gè)切實(shí)可以運(yùn)行的代碼。這個(gè)代碼和平時(shí)的代碼有所不同,那就是這個(gè)代碼是在系統(tǒng)已經(jīng)panic了以后被觸發(fā)的,此時(shí)的系統(tǒng)很可能全然混亂,因此此時(shí)最好的做法就是:
1.減少內(nèi)存操作;
2.不處理異常流;
3.盡可能簡(jiǎn)化,不再計(jì)算校驗(yàn)值;
4.避免ARP交互,直接廣播。
代碼的邏輯如下:
1.加載模塊:預(yù)先分配skb和get device,為了不在panic以后處理內(nèi)存;注冊(cè)通知鏈。
2.panic之后:調(diào)用通知鏈的通知操作,用最簡(jiǎn)化的方式廣播沒有校驗(yàn)碼的數(shù)據(jù)包。

代碼如下:


#include <linux/module.h>
#include <linux/skbuff.h>
#include <net/ip.h>

struct sk_buff * skb = NULL;
struct net_device * dev = NULL;

u8 ethhdr [ETH_HLEN] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x0c, 0x29, 0xdd, 0xaa, 0xfd,
        0x00, 0x00
};

u8 iphdr [20] = {
        0x45,
        0x00,
        0x00, 0x00,  //total length
        0x00, 0x00,  //ID
        0x40, 0x00,  //Don't fragment
        0x40,        //TTL
        0xff,        // UDP?
        0x00, 0x00,  //checksum
        0x02, 0x02, 0x02, 0x02,
        0xff, 0xff, 0xff, 0xff
};

u8 data[1024] = {0};

void do_nothing (struct sk_buff *skb)
{
    /* 不再進(jìn)行任何內(nèi)存操作 */
    //dev_put(skb->dev);
}

static int send_last_msg(struct notifier_block *self,
            unsigned long event, void *unused)
{
    int ret;
    struct ethhdr *eth;
    struct iphdr *ip;
    char *p;

    /* 獲取內(nèi)核緩沖區(qū)中的數(shù)據(jù) */
    kernel_log_buffer(data, sizeof(data));
 
    skb->dev = dev;
    skb->pkt_type = PACKET_OTHERHOST;
    skb->protocol = htons(ETH_P_IP);
    skb->ip_summed = CHECKSUM_NONE;
    skb->destructor = do_nothing;
    skb->priority = 0;

    /* 保留skb區(qū)域 */
    skb_reserve (skb, 2 + ETH_HLEN + sizeof(struct iphdr) + sizeof(data));

    /* 構(gòu)造數(shù)據(jù)區(qū)(使用UDP會(huì)比較好,但是懶的封裝了) */
    p = skb_push(skb, sizeof(data));
    memcpy(p, &data[0], sizeof(data));
    skb_reset_transport_header(skb);

    /* 構(gòu)造IP頭 */
    p = skb_push(skb, sizeof(struct iphdr));
    memcpy(p, &iphdr, sizeof(struct iphdr));
    ip = (struct iphdr*)p;
    ip->tot_len = htons(sizeof(data) + sizeof(struct iphdr));
    skb_reset_network_header(skb);

    /* 構(gòu)造以太頭 */
    p = skb_push(skb, sizeof(struct ethhdr));
    eth = (struct ethhdr*)p;
    eth->h_proto = htons(ETH_P_IP);
    memcpy(p, hdr, sizeof(struct ethhdr));
    skb_reset_mac_header(skb);

    /* 發(fā)射 */
    ret = dev_queue_xmit(skb);
    if (ret < 0) {
        /* 由于panic了,不再處理內(nèi)存,不處理異常流 */
        //kfree_skb (skb);
        //dev_put(dev);
        goto out;
    }
out:
    return ret;
}

static struct notifier_block on_panic_send = {
        .notifier_call = send_last_msg,
};

static int __init
panic_sendmsg_init(void)
{
    int ret = -1;
    dev = dev_get_by_name (&init_net, "eth3");
    if (!dev) {
        printk("Can't get device\n");
        goto out;
    }
    /* 不在通知鏈中分配,因?yàn)槟菚r(shí)已經(jīng)panic,故預(yù)先分配 */
    skb = alloc_skb(1500, GFP_ATOMIC);
    if (!skb) {
        dev_put(dev);
        printk("Alloc skb failed\n");
        goto out;
    }
    /* 注冊(cè) */
    ret = atomic_notifier_chain_register(&panic_notifier_list, &on_panic_send);
    if (ret) {
        dev_put(dev);
        kfree_skb (skb);
        printk("Register notifier chain failed\n");
        goto out;
    }

    return 0;
out:
    return ret;
}

static void __exit
panic_sendmsg_exit(void)
{
    atomic_notifier_chain_unregister(&panic_notifier_list, &on_panic_send);
    if (dev) {
        dev_put(dev);
    }
    if (skb) {
        kfree_skb (skb);
    }
}

module_init(panic_sendmsg_init);
module_exit(panic_sendmsg_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("marywangran <marywangran@126.com>");


需要指出的 是kernel_log_buffer接口,這個(gè)接口是我自己添加進(jìn)內(nèi)核并EXPORT出來的。我認(rèn)為這是必要的,printk將信息放在了內(nèi)核的一個(gè)緩 沖區(qū)中,這個(gè)緩沖區(qū)可以被用戶態(tài)進(jìn)程通過syslog系統(tǒng)調(diào)用讀取。我們知道,常規(guī)的辦法是內(nèi)核信息被syslog守護(hù)進(jìn)程讀取,然后發(fā)送到網(wǎng)絡(luò)或者保存 成文件。但是panic之后呢?既然用戶態(tài)已經(jīng)完全不再運(yùn)行,那么就不存在通過什么守護(hù)進(jìn)程存日志的希望了,當(dāng)然在內(nèi)核panic通知鏈鉤子中直接操作磁 盤日志文件是可以的,但是那太不可靠,因?yàn)榇疟P的IO邏輯同樣存在于內(nèi)核態(tài)代碼或者驅(qū)動(dòng)代碼中,此時(shí)內(nèi)核已經(jīng)panic了...雖然從網(wǎng)絡(luò)發(fā)送數(shù)據(jù)也同樣 要用到已經(jīng)panic的內(nèi)核執(zhí)行流,但是畢竟協(xié)議棧操作要比磁盤操作的調(diào)用深度要淺很多(數(shù)據(jù)的最終解釋,排序,保存操作都在對(duì)端進(jìn)行,磁盤IO則不 然)。

       不管怎么樣,不管是磁盤操作還是網(wǎng)絡(luò)操作,都需要將內(nèi)核log緩沖區(qū)中的內(nèi)存dump下來才好,然而內(nèi)核log緩沖區(qū)本身并沒有被EXPORT,它只能通過不多的幾個(gè)系統(tǒng)調(diào)用接口來獲取,而這些接口很難在內(nèi)核里面調(diào)用,因此我自己添加了一個(gè):


int kernel_log_buffer(char *buf, int max_len);


其中,buf為內(nèi)核log保存到的緩沖區(qū),max_len為buf的長(zhǎng)度,返回值為實(shí)際返回信息的長(zhǎng)度。這個(gè)接口完全類似于read系統(tǒng)調(diào)用。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI